Skip to content

Commit 7e28d43

Browse files
authored
chore(v0.3.1): add SECURITY.md / Sigstore attestations / pytest pythonpath / docs follow-up (#12)
Add the SECURITY.md the v0.1.1 CHANGELOG entry promised but never landed. Includes supported-version table (0.3.x supported, older unsupported), private reporting flow via GitHub Security Advisories, acknowledgement / fix SLAs, and the in-scope / out-of-scope split. Enable PEP 740 Sigstore attestations on PyPI publishes via pypa/gh-action-pypi-publish@release/v1 with: attestations: true. Downstream consumers can verify artifact provenance from 0.3.1 onwards without trust-on-first-use. Set [tool.pytest.ini_options].pythonpath = ["src"] so contributors running pytest immediately after a fresh clone do not hit the 17 KeyError failures that a stale system-installed wheel produces by shadowing the source tree. README pre-commit pin: v0.2.1 → v0.3.1 so pinning the example actually gets R012 / R013 / R014. README testing section: replaced "Aim for 45+ passing tests" with the current actual count (66) and an enumeration of rules covered. CHANGELOG v0.1.2: replaced placeholder [PR/discussion link] with the concrete PR reference. CHANGELOG v0.1.1 SECURITY.md line corrected with a parenthetical noting the file actually landed at 0.3.1. No rule logic changed; this release is byte-identical to 0.3.0 in behaviour. Consumers who already run 0.3.0 see no behavioural difference, but gain Sigstore-attested artifacts and a working pre-commit pin.
1 parent a26f4a8 commit 7e28d43

5 files changed

Lines changed: 159 additions & 7 deletions

File tree

.github/workflows/publish.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,8 @@ jobs:
8585

8686
- name: Publish to PyPI
8787
uses: pypa/gh-action-pypi-publish@release/v1
88+
with:
89+
attestations: true # PEP 740 Sigstore attestation; downstream consumers can verify the artifact origin without trust-on-first-use.
8890
# No `password` or `user` — OIDC trusted publishing handles auth.
8991
# Configure the trusted publisher on PyPI before the first release
9092
# this workflow ships (see header comment for the one-time setup).

CHANGELOG.md

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,48 @@
11
# CHANGELOG — claude-memory-lint
22

3+
## v0.3.1 — 2026-05-09 (patch follow-up)
4+
5+
### Added
6+
7+
* `SECURITY.md` — disclosure policy, supported-version table, the
8+
in-scope / out-of-scope list, and the hardening posture summary
9+
(atomic write, `.bak` mode 600, `realpath` confinement, OIDC
10+
publishing, privacy-scan + pre-commit secret scan). Earlier release
11+
notes referenced an "updated `SECURITY.md`" before the file
12+
actually existed; this release closes that gap and the relevant
13+
CHANGELOG line has been corrected.
14+
* PEP 740 attestations on PyPI publishes via
15+
`pypa/gh-action-pypi-publish@release/v1` `with: attestations: true`.
16+
Starting from this release, downstream consumers can verify
17+
artifact origin through Sigstore without trust-on-first-use.
18+
* `pyproject.toml` `[tool.pytest.ini_options].pythonpath = ["src"]`
19+
so a fresh clone runs `pytest` cleanly without the editable install.
20+
Without the line, contributors hit 17 `KeyError` failures because
21+
the system-installed older wheel shadows the source tree; the
22+
config change makes the source layout authoritative for the test
23+
run.
24+
25+
### Fixed
26+
27+
* `README.md` pre-commit example: `rev: v0.2.1``rev: v0.3.1` so
28+
pre-commit users actually pin a release that contains R012 / R013 /
29+
R014.
30+
* `README.md` testing section: replaced "Aim for `45+` passing tests"
31+
with the current actual suite size (66) and an enumeration of the
32+
rules covered. The "aim" wording predated multiple rule additions
33+
and had become misleading.
34+
* `CHANGELOG.md` v0.1.2 entry: replaced placeholder
35+
`[PR/discussion link]` with the concrete PR reference.
36+
37+
### Notes
38+
39+
No rule logic changed in this release; the rule registry, the
40+
default-on/off matrix, and the CLI surface are byte-identical to
41+
`v0.3.0`. This is a documentation / CI / build-process patch only,
42+
so consumers who already run `v0.3.0` get no behavioural difference
43+
from upgrading — but they do gain Sigstore-attested artifacts and a
44+
working pre-commit pin.
45+
346
## v0.3.0 — 2026-05-09 (minor)
447

548
### Added — three new rules from a second pollution-investigation wave
@@ -279,7 +322,8 @@ vocabulary-collision / opt-in-error-size) considered for this
279322
release were rejected during a 2-agent design review for being
280323
either user-specific symptom projections (not generalisable to the
281324
OSS user base) or implementations whose false-positive rate would
282-
overwhelm their signal — see [PR/discussion link] for the rationale.
325+
overwhelm their signal — see PR #4 (and adjacent design discussion)
326+
for the rationale and the rejected candidates list.
283327
The conservative outcome here is intentional: lint rules are easier
284328
to add than to retract, and v0.1.1 was already addressing 90%+ of
285329
the in-scope failure modes.
@@ -299,7 +343,10 @@ the in-scope failure modes.
299343
so CI integrations actually fail.
300344
- **CLI `_filter_rules`**: invalid rule names raise `ValueError`
301345
instead of silently disabling all rules.
302-
- **Atomic .bak**: tmpfile + replace, mode 600. SECURITY.md updated.
346+
- **Atomic .bak**: tmpfile + replace, mode 600. (`SECURITY.md` was
347+
referenced here at the time of v0.1.1 hardening but the dedicated
348+
policy document was not landed until `v0.3.1`; see that release for
349+
the actual disclosure policy.)
303350

304351
## v0.1.0 — 2026-05-08
305352

README.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ Add to your `.pre-commit-config.yaml`:
8585
```yaml
8686
repos:
8787
- repo: https://github.com/hinanohart/claude-memory-lint
88-
rev: v0.2.1
88+
rev: v0.3.1
8989
hooks:
9090
- id: claude-memory-lint
9191
args: [check, --rule, R001, --rule, R002]
@@ -132,10 +132,11 @@ pip install -e .[dev]
132132
pytest -v
133133
```
134134

135-
Aim for `45+` passing tests covering parser edge cases (no
135+
The current suite is `66` tests covering parser edge cases (no
136136
frontmatter / unterminated frontmatter / inline lists / multi-line
137-
lists), per-rule positive and negative cases, the corpus rule, and the
138-
CLI front-end including JSON / SARIF reporters.
137+
lists), per-rule positive and negative cases for R001-R014, the
138+
corpus rules R007 and R011, and the CLI front-end including JSON /
139+
SARIF reporters.
139140

140141
## License
141142

SECURITY.md

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
# Security Policy
2+
3+
## Supported versions
4+
5+
`claude-memory-lint` ships fixes only on the latest minor line. Older
6+
minor releases receive no security backports — upgrade to the latest
7+
release to get fixes.
8+
9+
| Version | Supported |
10+
| --- | --- |
11+
| `0.3.x` ||
12+
| `0.2.x` | ❌ — please upgrade |
13+
| `< 0.2` | ❌ — please upgrade |
14+
15+
## Reporting a vulnerability
16+
17+
**Please do not open a public GitHub issue for security problems.**
18+
Instead, file a private report through GitHub Security Advisories:
19+
20+
1. Open the [repository Security tab](https://github.com/hinanohart/claude-memory-lint/security/advisories/new).
21+
2. Click **"Report a vulnerability"** and describe what you found —
22+
reproduction steps, the affected version, and the impact you can
23+
demonstrate.
24+
3. Maintainers acknowledge new advisories within **7 days**, and aim
25+
to ship a fix within **30 days** of acknowledgement when the report
26+
is reproducible. Reports that turn out to be wider issues (e.g.
27+
upstream Python or runner bugs) are co-disclosed with the relevant
28+
project on a coordinated timeline.
29+
30+
If GitHub Security Advisories is unavailable for any reason, you may
31+
fall back to opening an issue titled "security: please contact me
32+
privately" without reproduction details, and a maintainer will reach
33+
out off-platform.
34+
35+
## In scope
36+
37+
We treat the following as security-relevant and welcome reports on
38+
them:
39+
40+
* **Secret leakage by the linter itself** — any path through which a
41+
matched secret literal reaches stdout, stderr, the JSON / SARIF
42+
output, the `.bak` file, log files, or any error message. R009 has
43+
defence-in-depth tests asserting the matched substring is never
44+
written to user-facing output; a regression there is the most
45+
serious class of bug this project can ship.
46+
* **Path-handling weaknesses** — any way to make `cml fix` write
47+
outside the directory it was pointed at, follow symlinks into
48+
unrelated trees, or overwrite files under a malicious target name.
49+
* **Backup-file leakage**`.bak` files written by `cml fix` are
50+
created with mode `600`. A regression that widens those permissions,
51+
or that writes `.bak` files outside the corpus directory, is in
52+
scope.
53+
* **Atomic-write regressions**`cml fix` writes through tmpfile +
54+
rename to avoid leaving a truncated file on `SIGKILL`. Any change
55+
that observably leaves a half-written `.md` is in scope.
56+
* **Privacy regressions** — the `stats` command and JSON output deliberately
57+
print only filenames, rule IDs, and counts. A change that leaks
58+
body content into any non-`text` reporter is in scope.
59+
* **Supply-chain integrity of releases** — issues with the OIDC-based
60+
PyPI publish workflow (see `.github/workflows/publish.yml`),
61+
including any change that lets a non-`main` branch produce a
62+
PyPI release, or that bypasses the test / lint / privacy-scan
63+
gates that run before publication.
64+
65+
## Out of scope
66+
67+
* Dependency advisories on transitively-installed packages that are
68+
development-only (e.g. `pytest`, `ruff`, `build`, `twine`). Report
69+
those upstream; we will follow when a fix is released.
70+
* Lint findings that you disagree with. Open a regular issue for
71+
rule-design feedback.
72+
* Theoretical attacks against secret patterns the linter does not
73+
claim to detect. R009 enumerates the supported pattern set; expanding
74+
it is enhancement work, not a security report.
75+
76+
## Hardening posture
77+
78+
Some defences are already in place and are exercised by the test
79+
suite or CI:
80+
81+
* **No LLM API calls.** The linter is heuristic-only; matched content
82+
never leaves the local machine.
83+
* **Atomic writes** for `cml fix` (`tmpfile + os.replace`).
84+
* **Mode 600** on `.bak` files and on the temp file used for atomic
85+
replacement.
86+
* **`realpath` confinement** in path resolution for `cml fix` so
87+
symlinks cannot redirect a write to an arbitrary tree (see
88+
`src/cml/cli.py` and the corresponding unit tests).
89+
* **Privacy-scan workflow** runs on every PR; see
90+
`.github/workflows/test.yml`. Catches token literals committed
91+
alongside unrelated changes.
92+
* **Pre-commit secret scan** (`pre-commit-security` hook) runs on the
93+
maintainer side as the last write-time gate before a commit lands
94+
on this repository.
95+
* **OIDC-based PyPI publishing** with `attestations: true` (PEP 740)
96+
starting at `0.3.1` — downstream installers can verify artifact
97+
provenance without long-lived API tokens.

pyproject.toml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
44

55
[project]
66
name = "claude-memory-lint"
7-
version = "0.3.0"
7+
version = "0.3.1"
88
description = "A lint for Claude Code memory directories — catches frontmatter rot, oversized files, and stop-word noise before it pollutes Claude's context."
99
readme = "README.md"
1010
requires-python = ">=3.10"
@@ -46,6 +46,11 @@ packages = ["src/cml"]
4646
[tool.pytest.ini_options]
4747
addopts = "-ra -q --strict-markers"
4848
testpaths = ["tests"]
49+
# Resolve `src/` layout without requiring an editable install; new
50+
# contributors can run `pytest` immediately after cloning instead of
51+
# discovering the editable-install requirement through 17 mysterious
52+
# KeyError failures.
53+
pythonpath = ["src"]
4954

5055
[tool.ruff]
5156
line-length = 100

0 commit comments

Comments
 (0)