docs(security): the attack surface here is data, so the report route had to say where a wrong entry gets fixed
org-ops recorded SECURITY.md as missing against the org standard (coord, 2026-08-11) and this repository owed it for a sharper reason than "given what the repo is about": nothing here runs, so a report is never a crash — it is a detection entry that looks like it works and is not looking. SECURITY.md therefore answers what an ordinary policy does not have to: how to report that a detection-table entry is WRONG, and why a confirmed defect in extracted data is decided in the runtime it was extracted from before it is changed here. Correcting it here would make the copy disagree with the implementation it was taken from — two runtimes, two answers on one input, the exact failure this repository exists to prevent. Two classes skip that routing: a real secret in the history, and data authored here rather than extracted. Fix latency is stated plainly as bounded by the owning runtime's schedule and the consumer's pull, not by ours. secret-egress 0.1.0 -> 0.2.0 is a staleness DISCLOSURE, not a data change: all 18 patterns byte-identical, one evidence_limits entry added. llm-security reports the source table at 19 entries now; recorded as their report and not reproduced, because the commit carrying it is not on their public remote — measured at b1ba1fb today. What was measured here: none of the 18 patterns matches a legacy sk-...T3BlbkFJ... shape. A consumer vendoring this file under-matches the seed hook by one entry, and now reads that in the file. manifest 0.3.0 -> 0.3.1 corrects the secret-egress blocker. Through 0.3.0 it named gcp-service-account-json and openai-api-key-legacy together as ids "absent here". Measured against the guard at e671edb by running this file's own 18 patterns over a service-account document: a COMPLETE service-account key file is matched here at order 11, since the PEM entry's prefix group is optional and the bare PKCS#8 header matches; the same document with private_key removed matches nothing here while the guard's marker still fires. That is a cut-point difference, which is what the blocker is about, not a missing entry. openai-api-key-legacy IS a real hole and is now recorded as one. Folded into the existing blocker string rather than a sibling key, because blockers is a map from table path to text. Verified: all JSON well-formed; every non-fixture JSON has a top-level version; charter clean (no executable code); patterns[] and count byte-identical to HEAD for secret-egress; manifest key set unchanged and count still 90; 90 case directories untouched; every spec still carries its normative marker. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01T85QqeiWBEoBMjWaMiBnxD
This commit is contained in:
parent
d96fbbf791
commit
0e765a02eb
5 changed files with 201 additions and 4 deletions
133
SECURITY.md
Normal file
133
SECURITY.md
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
# Security policy
|
||||
|
||||
This repository ships **no runnable code** — no package, no build, no dependency tree,
|
||||
nothing that executes on your machine. So the usual question, *can this be exploited*,
|
||||
has an unusual answer here: the attack surface is the **data**.
|
||||
|
||||
Seven data files here carry the detection material — pattern tables, code-point carriers,
|
||||
calibration thresholds, an OWASP mapping — and several independent runtimes read them at the
|
||||
same time. A wrong code point, a mis-escaped regex, a fixture that expects a miss: none of that
|
||||
crashes anything. It produces a detector that looks like it works and is not looking. That
|
||||
is the vulnerability class this policy is about, and a report of one is welcome even though
|
||||
no code changes as a result.
|
||||
|
||||
## Reporting
|
||||
|
||||
**Do not open a public issue.** A report here usually names an input that gets *past* a
|
||||
detector, and that is a working bypass against every consumer until it is closed.
|
||||
|
||||
Report privately by email:
|
||||
|
||||
- **hello@fromaitochitta.com**, with `SECURITY` at the start of the subject.
|
||||
|
||||
Pull requests are not the channel either — they are switched off on the canonical
|
||||
repository, and not as an oversight. This repository is vendored into independent runtimes
|
||||
that pin a tag; a change to detection data changes what those runtimes *find*. Such a change
|
||||
has to be coordinated with each consumer before it exists, which a merge button does not do.
|
||||
Fork-and-own is the supported path.
|
||||
|
||||
Please include:
|
||||
|
||||
- the file and the entry — its `name`, `order` or `id`, whichever that file uses;
|
||||
- the tag you read (`v0.3.0`, not "main");
|
||||
- the input that should have matched and does not, or the input that matches and should not;
|
||||
- what a consuming runtime actually does today, if you have measured it.
|
||||
|
||||
**Obfuscate live payloads.** Do not send a working credential or a live carrier. Spell
|
||||
invisible characters as code points the way the tables do (`"U+200B"`), and use placeholder
|
||||
key material — a report should not itself be a delivery mechanism.
|
||||
|
||||
## What counts as a vulnerability here
|
||||
|
||||
In scope — all of these are real reports:
|
||||
|
||||
1. **A detection entry that is a silent false negative.** A wrong code point, a regex whose
|
||||
escaping is wrong for the declared dialect, missing or wrong flags, a pattern that fails
|
||||
to compile in a documented engine and gets skipped rather than reported.
|
||||
2. **A conformance fixture that sanctions a miss.** `expected.json` is ground truth: a
|
||||
runtime that disagrees with it is deemed wrong. A fixture that expects too little makes
|
||||
every conforming runtime wrong identically, and the corpus will not catch it.
|
||||
3. **A normative clause that mandates unsafe behaviour.** The `spec/` files bind the
|
||||
implementations that consume them, so a weak rule propagates to all of them.
|
||||
4. **A real secret or personal data in the repository or its history.** The history is
|
||||
public in full.
|
||||
5. **Data that has gone stale against its declared source in a way that under-detects.**
|
||||
Each data file names its source in a `provenance` block. If that source has since added
|
||||
or corrected an entry, the copy here under-matches, and a consumer vendoring it is less
|
||||
protected than the runtime it was taken from.
|
||||
|
||||
Out of scope — documented boundaries, not vulnerabilities. See **Known limitations** and
|
||||
**Non-goals** in [README.md](README.md):
|
||||
|
||||
- a detection class absent from the tables entirely (coverage is the union of what the seed
|
||||
implementations detected, not of what exists);
|
||||
- a table implemented by only one runtime, and cases marked `not-applicable` for the others;
|
||||
- disagreement about a `calibration.json` threshold — the thresholds are published, the
|
||||
policy built on them belongs to the consumer;
|
||||
- a divergence already recorded in [`docs/lexicon-port-divergence.md`](docs/lexicon-port-divergence.md);
|
||||
- the five data files no fixture constrains, and the finite homoglyph map.
|
||||
|
||||
If you are unsure which side something falls on, report it privately anyway.
|
||||
|
||||
## Why a confirmed defect is usually not fixed here first
|
||||
|
||||
This is the part that differs from an ordinary repository, and it is worth reading before
|
||||
you conclude that a fix is being stalled.
|
||||
|
||||
Most data here is an **extraction**: a copy of a table that lives in a runtime, kept
|
||||
behaviour-identical to it on purpose. Correcting an entry here — even a genuinely wrong one
|
||||
— would make the copy disagree with the implementation it was taken from. Two implementations
|
||||
answering differently on the same input is precisely the failure this repository exists to
|
||||
prevent, so producing one as a *fix* would be self-defeating.
|
||||
|
||||
A confirmed defect in extracted data therefore travels:
|
||||
|
||||
1. the report reaches the maintainer here, privately;
|
||||
2. the owning runtime is identified — every data file names it in `provenance.source_repo`
|
||||
— and the report is routed there;
|
||||
3. the decision is taken **there**, where the pattern is under test against a real suite;
|
||||
4. once the source has moved, this repository **re-extracts** from a pinned public commit
|
||||
and tags a release;
|
||||
5. consumers pull that tag on their own schedule.
|
||||
|
||||
Stated plainly, because it affects you: fix latency is bounded by the owning runtime's
|
||||
schedule and by each consumer's pull, not by this repository's. If you need protection
|
||||
sooner than that, the fix belongs in your own runtime; this repository is where it becomes
|
||||
shared, not where it becomes real.
|
||||
|
||||
Two things do **not** take that route:
|
||||
|
||||
- **A real secret in the repository or its history** (class 4) is handled here, immediately.
|
||||
- **Data authored in this repository** rather than extracted — it is flagged as such where
|
||||
it occurs, for example `authored_payloads` in `conformance/manifest.json` — is this
|
||||
repository's own to correct.
|
||||
|
||||
The precedent is on the record. In `v0.3.0` a detection pattern changed value here for the
|
||||
first time, and it changed because the owning runtime had changed its own and this
|
||||
repository re-read the source — not because a reviewer here judged the old value wrong.
|
||||
`docs/lexicon-port-divergence.md` records a row where two runtimes still disagree and this
|
||||
repository deliberately did *not* pick a winner. Provenance is the ground for moving a
|
||||
value. Merit is not, and the day it becomes the ground, the guarantee is gone.
|
||||
|
||||
## Supported versions
|
||||
|
||||
Pre-1.0. Only the latest tag is fixed; there are no back-ported branches.
|
||||
|
||||
Consumers vendor this repository (`git subtree`, or a pinned copy) rather than installing
|
||||
it, so a fix reaches a consumer only when that consumer pulls the new tag. There is no CI in
|
||||
this organisation and nothing polls for updates. When a fix changes detection data, the
|
||||
maintainer notifies the known consumers directly — but their upgrade is their own action, on
|
||||
their own schedule.
|
||||
|
||||
Read the `CHANGELOG.md` entry before upgrading rather than the version number: in 0.x, a
|
||||
change to what a conforming runtime *finds* is still a minor bump.
|
||||
|
||||
## Disclosure
|
||||
|
||||
There is no formal embargo SLA here. The maintainer will acknowledge the report, agree a fix
|
||||
and disclosure timeline with the reporter, and credit the reporter in the `CHANGELOG.md`
|
||||
entry unless they prefer to remain anonymous.
|
||||
|
||||
If the report is a false negative in a table that has already shipped, the changelog entry
|
||||
will say what slipped through, in enough detail that a consumer still pinned to the older
|
||||
tag can judge whether it is exposed. Naming it is the point of fixing it.
|
||||
Loading…
Add table
Add a link
Reference in a new issue