llm-security/scanners/commons/SECURITY.md

7.2 KiB

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:

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:

  • 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;
  • 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.