# Security policy `llm-ingestion-guard` is a defensive library for LLM ingestion pipelines. Its own security posture matters: a flaw here can silently admit a poisoned artifact into a downstream corpus. Reports are welcome. ## Supported versions The project is `1.x`. Only the latest published version receives fixes; there are no back-ported security branches. Pin a version and watch the `CHANGELOG.md` `### Security` entries. **What `1.0.0` freezes, and what it does not.** The freeze is a semver promise about the *Python surface*: no name exported from `llm_ingestion_guard` is removed, renamed or given a different meaning without a `2.0.0`. It is **not** a promise that detection behaviour holds still. Severities, thresholds, lexicon entries and the dispositions they produce are calibration, and calibration moves in minor and patch releases — a payload that disposes `WARN` on `1.0.0` may dispose `FAIL_SECURE` on a later `1.x`, and that is a fix rather than a break. Pin a version if you depend on a specific grading, and assert on the disposition your policy requires rather than on a severity you happened to observe. ## Reporting a vulnerability **Do not open a public issue for a vulnerability.** Public disclosure before a fix gives an attacker a window against every downstream consumer. Instead, report it **privately** to the maintainer via the canonical repository on Forgejo: - Repository: `git.fromaitochitta.com/open/llm-ingestion-pipeline-security` - Contact the maintainer directly through that Forgejo instance (private message / maintainer contact) and mark the subject `SECURITY`. Please include: - affected version / commit, - a minimal reproduction (input → observed disposition/finding vs. expected), - the impact you see (e.g. a poisoned artifact that disposes `WARN` instead of `FAIL_SECURE`). Obfuscate any real payloads the same way the test corpus does — build attack strings from `chr(0x…)` fragments so the report itself does not ship a live carrier. ## What counts as a vulnerability In scope (a real finding): - a bypass of a **stated** control — e.g. an invisible carrier that reaches the persist gate without failing secure, a credential that egresses without a `decoded:egress:*` / `egress:*` label, a `guard()` path that fails *open*; - a `prepare_input` / `screen_output` code path that raises instead of failing closed; - a ReDoS or unbounded-resource input against the scanner. Out of scope (documented boundaries — see the **Known limitations** section of `README.md`, not vulnerabilities): - semantic / factual poisoning invisible to lexicon + entropy; - a HIGH finding in *trusted* prose disposing to `WARN` (§4.7 trust-scaling); - hex-wrapped (non-base64) secret egress; - multimodal / binary-layer carriers (OCR, font stego, VBA/macros, encrypted files); - the multilingual homoglyph-mix false positive; - a low `Severity` on an ordinary outward fetch — on the detection side 1.x does not separate *what was seen* from *how bad it is*, so read the finding `id` for the capability; - the input-cap asymmetry at `MAX_INPUT_CHARS`: surfaces that return content raise `OversizeInputError`, surfaces that return findings truncate and emit `active:oversize-input`. Past the cap, "no finding" means "not looked at". The last two are conceded for the whole of `1.x`, deliberately and in writing (`docs/LIMITATIONS.md`): closing either changes an exported symbol's meaning and is therefore a `2.0.0` change. The homoglyph false positive is conceded differently — no fix is promised, but it is calibration, so one may land in any `1.x` release. If you are unsure whether something is in scope, report it privately anyway. ## Disclosure This is a small project without a formal embargo SLA. The maintainer will acknowledge a report, agree a fix + disclosure timeline with the reporter, and credit the reporter in the `CHANGELOG.md` `### Security` entry unless they prefer to remain anonymous.