199 lines
11 KiB
Markdown
199 lines
11 KiB
Markdown
# Conventions
|
|
|
|
The rules a change to this repository is held to, in one place.
|
|
|
|
Nothing here is new policy. Every rule below was already being applied — some of it stated in
|
|
[README.md](README.md), some in [SECURITY.md](SECURITY.md), some only visible in the shape of
|
|
the files themselves. It is collected here because a convention that only exists in the
|
|
maintainer's head is not a convention an outside reader can meet, and because two of the
|
|
decisions this repository makes — that nothing here runs, and that pull requests are switched
|
|
off — look arbitrary until the reason is written down next to them.
|
|
|
|
This file binds **contributions to this repository**. It does not bind the runtimes that read
|
|
the data; that is what `spec/` is for, and those files say `Status: normative` and mean it.
|
|
|
|
## The charter: nothing here runs
|
|
|
|
**This repository contains no executable code, and it will not acquire any.**
|
|
|
|
Not permitted, without exception:
|
|
|
|
- `.mjs`, `.js`, `.ts`, `.py`, `.sh` — or any other file that implements detection, scanning,
|
|
normalisation, scoring or I/O;
|
|
- `package.json`, `pyproject.toml`, lockfiles, dependencies, build steps;
|
|
- an import of, or knowledge of, any framework, SDK or runtime;
|
|
- network access, model calls, dependence on the clock, or randomness.
|
|
|
|
Permitted: JSON data, normative specifications in Markdown, and conformance fixtures
|
|
(`input.txt` plus `expected.json`).
|
|
|
|
The reason is `fork-and-own`. A consumer on a runtime nobody here has thought of should be able
|
|
to vendor this repository without inheriting a single technology choice. A build step is a
|
|
technology choice; so is a test runner. The moment one exists, the set of runtimes that can
|
|
adopt this core shrinks to the set that tolerates it.
|
|
|
|
The consequence is that **this repository cannot validate itself**. There is no CI in this
|
|
organisation and nothing runs on push. The checks below are yours to run, and they are the only
|
|
ones there are.
|
|
|
|
## How a change gets in — and why not by pull request
|
|
|
|
Pull requests are switched off on the canonical repository at
|
|
`git.fromaitochitta.com/open/llm-security-commons`, and issues are not the reporting channel
|
|
either. That is deliberate, and the reason is stronger than a preference about tooling.
|
|
|
|
This repository is **vendored into independent runtimes** — a Claude Code plugin on Node/ESM, a
|
|
Python guard, an advisory surface — each pinning a tag. The contract between them is semver, and
|
|
a change to detection data changes what those runtimes *find*. A patch to a pattern table is not
|
|
a contribution that can be merged and then socialised; it is a contract change that has to be
|
|
coordinated with every consumer **before it exists**, because the moment it is tagged, the next
|
|
consumer to pull it gets different findings than the one that pulled yesterday. A merge button
|
|
does not do that, and nothing downstream of a merge button can.
|
|
|
|
So the routes in are:
|
|
|
|
1. **Fork and own it.** MIT, and an intended use rather than a tolerated one. If you need a
|
|
different value in your runtime, this is the fast path and it is fully supported.
|
|
2. **Report it privately** — see [SECURITY.md](SECURITY.md). A wrong entry in a detection table
|
|
is a silent false negative in every runtime that reads it, so a report about one is a
|
|
security report even though nothing here executes. That file also explains why a confirmed
|
|
defect in extracted data is usually decided in the runtime it came from before it changes
|
|
here.
|
|
|
|
If you maintain a consumer, the coordination channel is direct contact with the maintainer, not
|
|
this repository's forge surface.
|
|
|
|
## Data files (JSON)
|
|
|
|
- **Every JSON file carries a top-level `"version"`** — a semver string. No exceptions.
|
|
- **Every JSON file states what it is and where its data came from**, in a top-level
|
|
`"$comment"` or `"description"`. Provenance is not optional metadata here; it is what makes
|
|
the difference between a table and a rumour.
|
|
- 2-space indentation, LF line endings, a trailing newline, UTF-8 without BOM.
|
|
- **Code points are written as strings** — `"U+200B"` — never as the raw invisible character.
|
|
Review cannot see what it cannot render, and a reviewer who cannot see a character cannot
|
|
check it. The single exception is `conformance/*/input.txt`, which by definition contains the
|
|
actual bytes.
|
|
- **Keys are stable identifiers.** Consumers match on them. **Changing a key is a breaking
|
|
change** and is versioned as one.
|
|
|
|
The four files that carry detection material — `codepoints/carriers.json`,
|
|
`signatures/secret-egress.json`, `signatures/malware-signatures.json`,
|
|
`signatures/active-content.json` — take one further rule, which is the most important line in
|
|
this document:
|
|
|
|
> **They are changed only from verified source data** — a dump from the owning repository, the
|
|
> Unicode standard, published vendor documentation. Never from memory, never "filled in for
|
|
> consistency". A guessed code point or a regex with wrong escaping is a silent false negative:
|
|
> a detector that looks like it is working and is not looking. If an entry cannot be verified,
|
|
> leave it out, or mark it explicitly unverified in its `$comment`.
|
|
|
|
## Specifications (Markdown)
|
|
|
|
- A normative specification carries **`Status: normative`** at the top and uses RFC 2119 terms
|
|
(MUST / MUST NOT / SHOULD / MAY) in uppercase, deliberately. These files bind the
|
|
implementations that read them.
|
|
- An informative document (`docs/`) carries **`Status: informative`** and is **never ground
|
|
truth**. It records measurements, history and open disagreements; a runtime is not wrong for
|
|
disagreeing with one.
|
|
- Naming a file that does not exist yet is allowed where the layout is part of the contract —
|
|
`spec/decode-pipeline.md` is named in README.md and marked **Planned**. It is not a link, and
|
|
nothing depends on it. A normative spec guessed at would be worse than an absent one.
|
|
|
|
## Conformance cases
|
|
|
|
- One directory per case: `conformance/<case-id>/input.txt` and
|
|
`conformance/<case-id>/expected.json`.
|
|
- `<case-id>` is stable and descriptive. **Changing a case id is a breaking change** — a
|
|
published conformance result names it.
|
|
- `expected.json` is **ground truth**. If a runtime disagrees with it, the runtime is wrong.
|
|
- The one way that reverses: the fixture is proven wrong. Then the fixture changes **in its own
|
|
commit, with the reason written down** — never folded into a change that does something else,
|
|
because a fixture edit is the one edit that can make every conforming runtime wrong
|
|
identically.
|
|
- A case declares the data files it is `scope`d to. A runtime that does not implement a scoped
|
|
table reports the case `not-applicable` — a third verdict beside pass and fail, and one that
|
|
must be reported rather than dropped from the denominator. See
|
|
[`spec/conformance-corpus.md`](spec/conformance-corpus.md).
|
|
|
|
## When a value may change
|
|
|
|
Detection values do not change here because someone here judged them wrong. Three mechanisms
|
|
have moved a value so far, and each is recorded in the file itself rather than only in the
|
|
changelog:
|
|
|
|
1. **Re-extraction** — the owning runtime changed its own value, and this repository re-read the
|
|
source at a pinned public commit. Recorded in `source_fidelity.post_extraction_drift`.
|
|
2. **Retraction** — this repository described its own provenance wrongly. The stored value may
|
|
have been right all along; the account of where it came from was not. Recorded in
|
|
`source_fidelity.retracted`.
|
|
3. **Owner-directed authoring** — the owning runtime decided a value and asked this repository
|
|
to carry it, because the dependency has inverted: the source now reads *this* file and holds
|
|
no literal to re-read. Recorded separately, in `source_fidelity.owner_directed_changes`,
|
|
precisely because calling it drift would assert that the source moved and commons followed —
|
|
which would be false in the one direction that matters.
|
|
|
|
What is **not** on that list is merit. Data extracted from an implementation is kept
|
|
behaviour-identical to it on purpose, because a copy that disagrees with its source is the exact
|
|
failure this repository exists to prevent. Producing one as a *fix* would be self-defeating. If
|
|
you believe an extracted value is wrong, say so — and expect the decision to be taken in the
|
|
runtime where the pattern is under test.
|
|
|
|
Data **authored here** rather than extracted — conformance payloads, flagged as
|
|
`authored_payloads` in `conformance/manifest.json` — is this repository's own to correct.
|
|
|
|
## Versioning
|
|
|
|
Two version numbers, and they are not locked to each other:
|
|
|
|
- **The repository tag** (`vX.Y.Z`) versions **the contract**: the file set, the key names, the
|
|
case ids, the disposition semantics.
|
|
- **Each JSON file's own `"version"`** is bumped when *that file* changes.
|
|
|
|
What counts as which:
|
|
|
|
| Change | Bump |
|
|
| --- | --- |
|
|
| New data field, new entry | minor |
|
|
| Changed or removed key, case id, or layout | **major** — consumers break |
|
|
| A change to what a conforming runtime *finds* | minor in 0.x, and the changelog says so |
|
|
|
|
That last row is why **the changelog entry is the thing to read before upgrading, not the
|
|
version number**. Pre-1.0, a release that changes findings is still a minor bump; only the entry
|
|
tells you whether your assertions move.
|
|
|
|
Consumers vendor **a tag, never `main`** — a conformance result can only be attributed to a tag.
|
|
Nothing polls for updates; when a change moves detection data, the maintainer notifies known
|
|
consumers directly, and their upgrade remains their own action on their own schedule.
|
|
|
|
## Checks to run before proposing a change
|
|
|
|
There is no CI. These four are the validation surface, they run offline in a second, and each
|
|
one has been confirmed to go red on a violation rather than merely green on a clean tree.
|
|
|
|
```bash
|
|
# 1. Every JSON file is well-formed
|
|
find . -name '*.json' -not -path './.git/*' -print0 \
|
|
| xargs -0 -n1 python3 -m json.tool > /dev/null && echo OK
|
|
|
|
# 2. Every JSON file outside conformance/ carries a top-level "version"
|
|
find . -name '*.json' -not -path './.git/*' -not -path './conformance/*' -print0 \
|
|
| xargs -0 python3 -c 'import json,sys
|
|
missing=[p for p in sys.argv[1:] if "version" not in json.load(open(p))]
|
|
print("\n".join("MISSING version: "+p for p in missing) or "OK")'
|
|
|
|
# 3. Every spec carries its normative marker (prints offending files, nothing = clean)
|
|
grep -L 'Status: normative' spec/*.md || echo OK
|
|
|
|
# 4. Charter guard: no executable code has crept in
|
|
find . -type f \( -name '*.mjs' -o -name '*.js' -o -name '*.ts' -o -name '*.py' -o -name '*.sh' \) \
|
|
-not -path './.git/*' | grep . && echo 'CHARTER VIOLATION' || echo OK
|
|
```
|
|
|
|
They are written as shell one-liners rather than shipped as a script because a script would be
|
|
`.sh`, and check 4 would then fail on the tooling meant to enforce it.
|
|
|
|
What they do **not** check: whether a value is *correct*. Nothing offline can. That is what the
|
|
conformance corpus is for, and it runs in each consumer's own test suite against a pinned tag —
|
|
constraining two of the seven data files, which is a real limit and is stated in
|
|
[README.md](README.md) under **Known limitations**.
|