Skip to content

Commit 3e37e23

Browse files
VortexUKclaude
andcommitted
Polish pass: LICENSE, SECURITY, CONTRIBUTING, templates, CodeQL, coverage gate
OSS hygiene files: - LICENSE: MIT, copyright 2026 VortexUK. README was previously "License: TBD" which blocks legitimate downstream use. - SECURITY.md: how to privately report vulns via GitHub Security Advisories. Documents the threat model (API token as password, DPAPI-at-rest, HTTPS-only constraints) so reporters know what is in vs out of scope. Links the v0.1.4 + v0.1.5 audit commits. - CONTRIBUTING.md: light dev-setup + PR checklist. Reinforces the Core/UI assembly split rule (new code goes in Core unless it genuinely needs ACT types) and reminds contributors that the maintainer owns version bumps. - .github/ISSUE_TEMPLATE/: bug_report.yml + feature_request.yml with structured fields (plugin/ACT/Windows versions for bugs, problem/solution/scope for features). config.yml disables blank issues and adds contact links for security + EQ2Lexicon website routing. CodeQL workflow: - .github/workflows/codeql.yml runs on push, PR, and Mondays at 06:00 UTC. Uses the security-and-quality query pack. Fetches ACT for the autobuild step so the whole sln analyzes (CodeQL on Core alone would miss UI-layer findings). Findings land under the repo Security tab. Coverage threshold (>= 70% line coverage): - New ci.yml step parses ReportGenerator's Summary.txt and fails the build below 70%. coverlet.collector doesn't expose a threshold flag (coverlet.msbuild does, but we are pinned to the collector for net48 compat after the coverlet v10 incompatibility documented in dependabot.yml). - Current coverage is 72.8% line / 80.4% branch — thin margin above the gate; new untested code will need accompanying tests. README: - Adds CodeQL + MIT license badges next to the existing CI + release badges. - "License: TBD" replaced with link to LICENSE. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 8079c25 commit 3e37e23

9 files changed

Lines changed: 317 additions & 1 deletion

File tree

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
name: Bug report
2+
description: Something the plugin does wrong, or fails to do.
3+
labels: ["bug"]
4+
body:
5+
- type: markdown
6+
attributes:
7+
value: |
8+
Thanks for taking the time to file a bug. A complete report makes it dramatically more likely to get fixed quickly.
9+
10+
**Security issue?** Don't file it here — use [GitHub Security Advisories](https://github.com/VortexUK/EQ2LexiconACTPlugin/security/advisories/new) instead.
11+
12+
- type: input
13+
id: plugin-version
14+
attributes:
15+
label: Plugin version
16+
description: From ACT's Plugin Listing, or the filename you downloaded.
17+
placeholder: "v0.1.6"
18+
validations:
19+
required: true
20+
21+
- type: input
22+
id: act-version
23+
attributes:
24+
label: ACT version
25+
description: From ACT's title bar, or Help → About.
26+
placeholder: "3.8.5.288"
27+
validations:
28+
required: true
29+
30+
- type: input
31+
id: os
32+
attributes:
33+
label: Windows version
34+
placeholder: "Windows 11 Pro 10.0.26200"
35+
validations:
36+
required: true
37+
38+
- type: textarea
39+
id: what-happened
40+
attributes:
41+
label: What happened
42+
description: What did you do, what did you see, and what did you expect instead?
43+
placeholder: |
44+
1. Killed a trash mob in Hadden's Earthen Cave
45+
2. Expected an upload status of "uploaded (X)" in the Last Captured card
46+
3. Saw "error: ..." instead
47+
validations:
48+
required: true
49+
50+
- type: textarea
51+
id: settings-panel
52+
attributes:
53+
label: What does the Last Captured card show?
54+
description: |
55+
Open ACT → EQ2 Lexicon tab → Last Captured card. Paste the encounter title, time, combatant count, attack-type count, and upload status verbatim.
56+
**Do not include your API token anywhere in this issue.**
57+
validations:
58+
required: false
59+
60+
- type: textarea
61+
id: payload
62+
attributes:
63+
label: Captured payload (optional)
64+
description: |
65+
If the bug is about payload contents, click "Show payload" on the Last Captured card and paste a redacted sample. Strip your character name if you'd rather keep it private.
66+
render: json
67+
validations:
68+
required: false

.github/ISSUE_TEMPLATE/config.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
blank_issues_enabled: false
2+
contact_links:
3+
- name: Security vulnerability
4+
url: https://github.com/VortexUK/EQ2LexiconACTPlugin/security/advisories/new
5+
about: Report a security issue privately. Do not file a public issue.
6+
- name: EQ2 Lexicon website issue
7+
url: https://github.com/VortexUK/EQ2CensusBot/issues
8+
about: Issues with the website, account, or guild detection belong in the EQ2CensusBot repo.
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
name: Feature request
2+
description: Propose a new behaviour or improvement to existing behaviour.
3+
labels: ["enhancement"]
4+
body:
5+
- type: markdown
6+
attributes:
7+
value: |
8+
Have a look at [open issues](https://github.com/VortexUK/EQ2LexiconACTPlugin/issues?q=is%3Aissue) first — it might already be tracked.
9+
10+
For non-trivial changes, expect a conversation about scope before any code lands. See [CONTRIBUTING.md](https://github.com/VortexUK/EQ2LexiconACTPlugin/blob/main/CONTRIBUTING.md).
11+
12+
- type: textarea
13+
id: problem
14+
attributes:
15+
label: What problem are you trying to solve?
16+
description: Describe the situation that prompted the request. "I'd like X" is less useful than "When I do Y, I can't see Z and have to work around it by W."
17+
validations:
18+
required: true
19+
20+
- type: textarea
21+
id: proposed-solution
22+
attributes:
23+
label: Proposed solution
24+
description: What change to the plugin would solve it? Be as specific as you can.
25+
validations:
26+
required: true
27+
28+
- type: textarea
29+
id: alternatives
30+
attributes:
31+
label: Alternatives considered
32+
description: Other approaches you thought about and why this one is better.
33+
validations:
34+
required: false
35+
36+
- type: dropdown
37+
id: scope
38+
attributes:
39+
label: Where does this live?
40+
options:
41+
- Plugin (this repo)
42+
- EQ2 Lexicon website (EQ2CensusBot repo)
43+
- Not sure
44+
validations:
45+
required: true

.github/workflows/ci.yml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,27 @@ jobs:
101101
}
102102
Get-Content "TestResults/CoverageReport/Summary.txt"
103103
104+
# Enforce a minimum line-coverage threshold. We parse the
105+
# ReportGenerator summary because coverlet.collector doesn't
106+
# expose a per-run threshold flag (coverlet.msbuild does, but
107+
# we're on the collector for net48 compat).
108+
- name: Coverage threshold (line >= 70%)
109+
shell: pwsh
110+
run: |
111+
$summary = Get-Content "TestResults/CoverageReport/Summary.txt" -Raw
112+
# Match e.g. "Line coverage: 87.3%". The regex captures the float.
113+
if ($summary -match 'Line coverage:\s*([0-9]+(?:\.[0-9]+)?)%') {
114+
$coverage = [double]$Matches[1]
115+
Write-Host "Line coverage: $coverage%"
116+
if ($coverage -lt 70) {
117+
Write-Error "Coverage $coverage% is below the 70% threshold"
118+
exit 1
119+
}
120+
} else {
121+
Write-Error "Could not parse line coverage from Summary.txt"
122+
exit 1
123+
}
124+
104125
- name: Upload coverage report
105126
uses: actions/upload-artifact@v7
106127
if: always()

.github/workflows/codeql.yml

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
name: CodeQL
2+
3+
# GitHub-native semantic security scanning for the C# code. Runs on
4+
# every push + PR to main, plus a weekly schedule so newly-disclosed
5+
# CodeQL rules catch existing code. Findings appear under the repo
6+
# Security tab. Complements the dependency CVE scan that pre-push
7+
# and CI already run.
8+
on:
9+
push:
10+
branches: [main]
11+
pull_request:
12+
branches: [main]
13+
schedule:
14+
# Mondays at 06:00 UTC.
15+
- cron: '0 6 * * 1'
16+
17+
permissions:
18+
actions: read
19+
contents: read
20+
security-events: write
21+
22+
jobs:
23+
analyze:
24+
name: Analyze C#
25+
runs-on: windows-latest
26+
timeout-minutes: 30
27+
28+
steps:
29+
- uses: actions/checkout@v6
30+
31+
# The UI assembly references ACT.exe, so we need to fetch ACT
32+
# for CodeQL's autobuild step to succeed across the whole sln.
33+
- name: Fetch ACT for build reference
34+
shell: pwsh
35+
run: |
36+
$url = "https://github.com/EQAditu/AdvancedCombatTracker/releases/latest/download/ACTv3.zip"
37+
Invoke-WebRequest -Uri $url -OutFile ACTv3.zip -UseBasicParsing
38+
Expand-Archive -Path ACTv3.zip -DestinationPath C:\ACT -Force
39+
if (-not (Test-Path "C:\ACT\Advanced Combat Tracker.exe")) {
40+
throw "ACT exe not found in extracted zip"
41+
}
42+
"ACT_INSTALL_DIR=C:\ACT" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
43+
44+
- name: Setup .NET SDK
45+
uses: actions/setup-dotnet@v5
46+
with:
47+
global-json-file: global.json
48+
49+
- name: Initialize CodeQL
50+
uses: github/codeql-action/init@v4
51+
with:
52+
languages: csharp
53+
# security-and-quality includes the standard security queries
54+
# plus the broader quality ruleset.
55+
queries: security-and-quality
56+
57+
- name: Build
58+
run: dotnet build EQ2Lexicon.ACTPlugin.sln -c Release
59+
60+
- name: Perform CodeQL Analysis
61+
uses: github/codeql-action/analyze@v4
62+
with:
63+
category: /language:csharp

CONTRIBUTING.md

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# Contributing
2+
3+
Thanks for considering a contribution. This is a small plugin maintained mostly by one person, so the process is light.
4+
5+
## Before you start
6+
7+
For non-trivial changes, please [open an issue](https://github.com/VortexUK/EQ2LexiconACTPlugin/issues/new/choose) first to discuss the approach. This saves both of us time if the change isn't a fit for the project's direction.
8+
9+
Small fixes (typos, doc improvements, obvious bug fixes) can go straight to a PR.
10+
11+
For security issues, see [SECURITY.md](SECURITY.md) — please don't file public issues for vulnerabilities.
12+
13+
## Dev setup
14+
15+
See [README.md → Building from source](README.md#building-from-source) for prerequisites and build commands. In short:
16+
17+
1. Install .NET Framework 4.8 Developer Pack + .NET SDK 8.0.421 (pinned by [global.json](global.json)).
18+
2. Have ACT installed at the default path (or set `ACT_INSTALL_DIR`).
19+
3. Clone the repo.
20+
4. Activate the pre-push hook:
21+
```powershell
22+
git config core.hooksPath .githooks
23+
```
24+
25+
That hook runs format check + build + tests + vulnerability scan on every `git push`. It's the same gate CI runs.
26+
27+
## Code conventions
28+
29+
- `.editorconfig` is the source of truth. `dotnet format` will fix most things; `dotnet format --verify-no-changes` is what CI checks.
30+
- IDE0055 (formatting) is promoted to error so style drift fails the build.
31+
- Two assemblies, one solution:
32+
- `src/Core/EQ2Lexicon.ACTPlugin.Core.csproj` — pure code (no ACT or WinForms refs). Goes here unless you genuinely need ACT's types.
33+
- `src/EQ2Lexicon.ACTPlugin.csproj` — ACT-coupled UI layer. The integration boundary.
34+
- Tests reference Core only — UI types aren't unit-testable (they're integration-tested by running the DLL in ACT).
35+
- Comments are reserved for non-obvious *why*, not *what*. Lean on names.
36+
37+
## PR checklist
38+
39+
- [ ] Pre-push hook passes locally (format / build / tests / vuln scan)
40+
- [ ] New behaviour is covered by xUnit tests where the code is in Core
41+
- [ ] Public-facing changes are reflected in [CLAUDE.md](CLAUDE.md) and/or [README.md](README.md)
42+
- [ ] Commit messages explain the *why*, not just the *what*
43+
- [ ] Bumping `<Version>` is the *maintainer's* job — leave it alone in your PR
44+
45+
## Releasing (maintainer only)
46+
47+
See [README.md → Releasing](README.md#releasing). One tag push, the workflow does the rest.

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2026 VortexUK
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
# EQ2 Lexicon — ACT Plugin
22

33
[![CI](https://github.com/VortexUK/EQ2LexiconACTPlugin/actions/workflows/ci.yml/badge.svg)](https://github.com/VortexUK/EQ2LexiconACTPlugin/actions/workflows/ci.yml)
4+
[![CodeQL](https://github.com/VortexUK/EQ2LexiconACTPlugin/actions/workflows/codeql.yml/badge.svg)](https://github.com/VortexUK/EQ2LexiconACTPlugin/actions/workflows/codeql.yml)
45
[![Latest release](https://img.shields.io/github/v/release/VortexUK/EQ2LexiconACTPlugin)](https://github.com/VortexUK/EQ2LexiconACTPlugin/releases/latest)
6+
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
57

68
An [Advanced Combat Tracker](https://advancedcombattracker.com) plugin that uploads parsed EverQuest 2 encounters to the [EQ2 Lexicon](https://eq2lexicon.up.railway.app) website automatically after each fight ends.
79

@@ -116,4 +118,4 @@ gh release edit vX.Y.Z --draft=false
116118

117119
## License
118120

119-
TBD.
121+
[MIT](LICENSE).

SECURITY.md

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# Security Policy
2+
3+
## Reporting a vulnerability
4+
5+
**Please do not report security issues via public GitHub issues.**
6+
7+
If you've found a security issue in this plugin, report it privately via [GitHub Security Advisories](https://github.com/VortexUK/EQ2LexiconACTPlugin/security/advisories/new) on this repo. That creates a private channel between you and the maintainer to coordinate a fix before public disclosure.
8+
9+
You should expect an initial response within 7 days. If the issue is confirmed, a fix typically lands in a patch release within 2 weeks.
10+
11+
## Threat model
12+
13+
This plugin runs as a .NET 4.8 DLL loaded into the user's local ACT process at the user's Windows privilege level. We don't try to defend against an attacker who already has code execution on the user's machine — that's already game over.
14+
15+
The most sensitive asset is the user's **API token**, which grants upload + delete access to their own parses on the EQ2 Lexicon site. Treat it as a password. The plugin:
16+
17+
- Stores it DPAPI-encrypted at rest (`CurrentUser` scope), with a `DPAPI:` prefix in the config XML. Legacy plaintext from v0.1.0–v0.1.4 still loads but gets re-wrapped on next save.
18+
- Sends it only as a `Bearer` header to the configured `ServerUrl`, which `UploadClient.ValidateServerUrl` constrains to `https://` (or `http://` to `localhost` / `127.0.0.1` / `[::1]` for dev).
19+
- Never echoes it in status labels, error messages, log lines, or the "Show payload" dialog.
20+
21+
The server response is parsed by a deliberately narrow hand-rolled `ExtractJsonString` (not a full JSON parser). It's bounded by the response body size and only reads the single `detail` / `status` / `discord_name` string fields.
22+
23+
## In scope
24+
25+
- API token exfiltration paths in plugin code
26+
- TLS / certificate validation bypasses
27+
- Injection paths through the payload-building or response-parsing code
28+
- Privilege escalation beyond the user's normal Windows context
29+
- Disclosure of unsanitised user data in upload payloads
30+
31+
## Out of scope
32+
33+
- Anything that requires existing code execution on the user's machine
34+
- Vulnerabilities in ACT itself (report those to [EQAditu/AdvancedCombatTracker](https://github.com/EQAditu/AdvancedCombatTracker))
35+
- Vulnerabilities in the EQ2 Lexicon server (those belong in [VortexUK/EQ2CensusBot](https://github.com/VortexUK/EQ2CensusBot))
36+
- SmartScreen warnings on first install (the DLL is unsigned by design)
37+
38+
## Past security audits
39+
40+
- **v0.1.4** ([9eb39e0](https://github.com/VortexUK/EQ2LexiconACTPlugin/commit/9eb39e0)) — initial hardening pass: HTTPS-only enforcement, response-size bounds, status-message sanitisation.
41+
- **v0.1.5** ([5f9e11a](https://github.com/VortexUK/EQ2LexiconACTPlugin/commit/5f9e11a)) — DPAPI wrapping for the API token at rest.

0 commit comments

Comments
 (0)