Commit graph

22 commits

Author SHA1 Message Date
55ce6265d4 docs(plan): state the gate requirement precisely -- fixture set, not tagged artifact
okf asked outright whether the v1.0 gate requires 'the tagged artifact resolves
0.3.1' or 'the fixture set passes against 0.3.1'. It is the second, and the
answer is locked here so a later session cannot quietly upgrade the ask into a
release request against their operator.

They refused to widen their published range to run the measurement, and the
refusal is better than the ask it answers: widening a published range in order
to generate the evidence that would justify widening it is circular, and a red
result would then have meant unwinding a release to fix a test setup. The
measurement has to be able to say no. Their scratch-venv method -- outside the
project environment, --no-deps, guard installed from the v0.3.1 tag, resolved
version asserted from importlib.metadata -- satisfies the gate exactly.
2026-07-25 20:02:41 +02:00
6b705736cd docs(plan): record two consumer notification promises as release constraints
Both were given to named consumers who are building on current behaviour, and
both were about to live only in STATE.md -- which is gitignored, so a promise
kept there does not survive a machine. Breaking either silently is a release
defect, not a preference.

1. Any change to how PRESET_USER_UPLOAD grades ordinary links and remote images
   requires notice to linkedin-studio before it ships; they pin v0.3.1 at wiring.
   Applies to 0.4.0: the axis separation must change disposition, not grading.
2. Closing the relative-target asymmetry requires notice to llm-ingestion-okf;
   it is a Door C property rather than a guard gap, but closing it changes what
   arrives at their persist gate.

Also records a mechanism we had not modelled: okf reports percent-escapes
reaching filenames through a slugger, so %20 is manufactured systematically from
document titles rather than being incidental as it is in the documentation URLs
the FP was calibrated against.
2026-07-25 20:00:25 +02:00
d39033676a docs(plan): the v1.0 gate cannot be opened by a green result alone
okf pins llm-ingestion-guard >=0.2,<0.3 in [project].dependencies and pins the
uv source to tag v0.2.0 -- verified at their v0.4.0 tag and at their HEAD. The
cap excludes 0.3.1, so the committed Door B+C fixture run cannot reach the
version the gate names. It does not fail either: uv resolves v0.2.0, the
constraint holds, and the fixtures come back green because v0.2.0 never had the
regression. Green from an unmodified tree is the one outcome that proves nothing
and reads as proof.

The gate now requires the result to name the resolved guard version, and records
that transitive arrival does not count as an integration -- okf v0.4.0 makes the
guard a mandatory runtime dependency, so a consumer can acquire us without
choosing us, and today lands on v0.2.0 without the 0.3.0 hardening or the 0.3.1
fix. Silent under-defend, not a break.
2026-07-25 19:57:41 +02:00
10a37d104e docs(plan): retarget the Session G integration gate at v0.3.1 2026-07-25 15:45:45 +02:00
207b2c1679 docs(plan): record Session H as landed — 0.3.1 tagged with measurements 2026-07-25 15:45:14 +02:00
6e9b8168e3 fix(calibration): grade active content on URL shape, not construct type
v0.3.0 made the untrusted upload path unusable: measured on both doors, an
ordinary remote image fail_secure'd and an ordinary link/autolink/refdef
quarantined, so only documents without external references persisted.

Two independent defects compounded; neither fix works alone:

1. `markdown-image: HIGH` fired on any external image. The exfil primitive is a
   URL that moves bytes outward, not an image. `is_ordinary_url` now grades on
   shape - http(s)/protocol-relative, no query, no userinfo, no percent-escape,
   no opaque host label or path segment -> LOW; anything data-carrying keeps the
   carrier's severity. raw-html and data: URIs stay HIGH unconditionally.
   Opacity reuses entropy's primitives; floors calibrated against real doc URLs
   (worst legit token H=4.08, exfil segments 4.36-4.54) and frozen in
   calibration.

2. The quarantine_default floor fired on ANY finding, a premise that broke when
   every ordinary link became a finding. It now fires at MEDIUM+ - a no-op for
   every detector that shipped before 0.3.0 (no LOW/INFO exists), which is what
   makes this a patch rather than a minor.

The corpus blind spot that let this pass 522 green tests is closed: the FP
corpus carries realistic markdown and is asserted on the OUTPUT gate under
PRESET_USER_UPLOAD, with a counter-corpus of exfil-shaped URLs that must still
block. Beaconing and short opaque segments are conceded in LIMITATIONS and
asserted by the coverage matrix rather than papered over.

No new public API; no new preset (0.4.0 work); allow_reserved default unchanged.
2026-07-25 15:36:02 +02:00
da7421e6c8 docs(plan): scope Session H — 0.3.1 regression fix for active-content calibration
v0.3.0 makes the untrusted upload path refuse ordinary documents. Measured on
both doors under PRESET_USER_UPLOAD: one ordinary markdown image reaches
fail_secure, one ordinary link/autolink/refdef reaches quarantine_review, and
only a document with no external references persists.

Root cause is three independent defects that compound. Severity tracks
construct type rather than URL shape, so every external image is HIGH when the
exfiltration primitive is actually a URL that carries data outward. The
quarantine floor fires on any finding at all, a premise that broke once every
link became a finding. And the false-positive corpus could not have caught
either: it holds no markdown links or images, asserts only under
PRESET_TRUSTED_SOURCE where everything warns anyway, and runs _scan_input, so
the output gate where active_content lives is never exercised by it.

Both code changes are required together -- verified that fixing severity alone
still quarantines via the floor, and fixing the floor alone still fail-secures
on a HIGH image.

Records the version reasoning too: 0.3.1 is honest as a patch because the
lexicon carries no LOW/INFO patterns (40 high, 22 medium, 21 critical) and no
other detector emits LOW, so raising the floor to MEDIUM+ is a no-op for every
finding that existed before 0.3.0. The new middle preset stays out of this
release; it is additive API and belongs in 0.4.0.
2026-07-25 15:11:12 +02:00
7025759789 docs(plan): record the revised v1.0 gate and the 0.3.0 cut ahead of Session G
PLAN-v1.md said consumer integration was NOT part of the v1.0 sequence, and
Session G listed A-F as its only dependency -- both of which are now false.
The gate change lived only in STATE.md, which is local-only and overwritten
each session, so the plan was the wrong side of the truth.

Records: 1.0.0 now gates on the first real integration coming back green
(llm-ingestion-okf step 4 against v0.3.0), not on our own suite; the reasoning
(522 self-authored tests prove the code matches the design, not that the
design survives contact -- our first external contact found both a
tag-vs-main divergence and three undocumented behaviour changes); and that
one validated `integrated` outweighs five `planned` declarations, so we do not
wait on repos that may never arrive.

Also fixes Session G's now-stale mechanics: its CHANGELOG entry can no longer
"list A-F" because A/A2/B already shipped under [0.3.0]; the version-sync file
list gains the README status line and install pin; and verification gains an
anonymous clean-venv install check against the new tag, since README must
never advertise a command we have not run.
2026-07-25 12:27:01 +02:00
3dda1f68fa docs(readme): add concrete 'What it protects against' catalogue; split limitations
Answers the gap that the README said what it does NOT stop (a long limitations
section) but never plainly listed what it DOES. Add a 'What it protects against'
section high up: attack classes grouped by OWASP anchor (LLM01 injection + 83
lexicon classes + carriers, LLM02 egress, LLM05 EchoLeak, LLM06 agency, LLM10
fail-secure, OKF T1-T6, container front-end), each driven by a live coverage-matrix
payload. Move the full honest-limitations list to docs/LIMITATIONS.md; README keeps
a high-impact summary + link. Net: protection and limits read in balance, 261 -> 216
lines. Coverage 126/126 and 522 tests unchanged; every class listed is real.
2026-07-15 19:09:04 +02:00
a1f3fe1983 docs(adoption): reusable consumer adoption brief for OKF second-brain repos
Self-contained brief a consumer repo can plan an inclusion from: what the
guard is (write-time, not query-time), the two bookends + 8-step contract,
the shipped OKF adapter (import_bundle mode-b, per-concept gates), how to
verify (coverage matrix -> 126 classes), how to depend (stdlib-only core),
and a planning checklist for WHEN/WHERE to wire it (untrusted boundary, not
first-party onboarding). Every claim verified against v0.2 code.
2026-07-15 12:35:09 +02:00
66f3cbf4f5 docs(pdf): concede .pdf as a deliberate design boundary (Session F1)
Reframe .pdf from a 'known gap'/TODO to a deliberate concession across the
honest-limits and OKF-upload docs. A top-level .pdf drop is already refused as
an unsupported format (inbox_frontend.py raises on the else branch), not
half-scanned; adding a PDF parser + reportlab (solely to author white-on-white
test fixtures) is disproportionate for a dev-scoped showcase, and the OCR /
font-render stego carriers a PDF would smuggle are out of scope regardless.

- README honest-limits: .pdf = concession, not TODO; only the numeric CSV FP
  remains a known gap.
- docs/PLAN.md: upload table .pdf row marked 'conceded'; honest-scope paragraph
  names .pdf; assertions tightened to 'every accepted format'.

Closes the last format gap before v1.0 freeze (Session G). No code touched;
357 tests green.
2026-07-15 10:15:39 +02:00
1625f3893b docs: version-sync + SECURITY/CONTRIBUTING + honest-limits (Session E)
- README: tests badge 275->357; status v0.1->v0.2 (repo is 0.2.0; the v1.0
  bump belongs to the Session G freeze, not this docs pass); add three
  honest-limits — lone-HIGH-in-trusted-prose->WARN, vacuous quarantine-floor,
  Cyrillic/Latin homoglyph-mix false positive.
- docs/BRIEF.md: drop "No code yet" pre-implementation framing -> implemented v0.2.
- docs/OKF-INGESTION-BRIEF.md 4: correct cross-link control language —
  absolute https / references/ targets are spec-permitted, not rejected.
- Add SECURITY.md (private Forgejo disclosure) + CONTRIBUTING.md (stdlib-only
  core, Iron-Law TDD, no trailers, Forgejo-only invariants).
2026-07-15 10:08:24 +02:00
4a9cfd2bbe docs: reframe novelty claim to composite write-time contract (review MAJOR #3)
Replace the unverified/absolute novelty statement with the defensible
four-part-contract form, verified against a focused adversarial PyPI+GitHub
survey (2026-07-15):

- BRIEF §11: 'assumed, not verified' -> verified survey with sources. Names
  aig-guardian (real, query-time; blurs only the minimal-dep-library
  differentiator), GuardLLM (nearest neighbour, runtime hardening, no
  scan-before-persist / capability isolation / fail-secure), and ipi-scanner
  (orphaned placeholder repo, recorded for honesty not as prior art).
- README: differentiator moved from 'library vs hosted/model' to the full
  four-part write-time contract.
- PLAN §27-31: drop the unverifiable 'the first' superlative.

Also promotes the v1.0 session plan (PLAN-v1.md) and the cross-model review
(review-2026-07.md) into docs/ on the open/ mirror, referenced by PLAN.md's
re-sequencing addendum.
2026-07-15 09:22:38 +02:00
896ab4034a docs(plan): showcase covers realistic upload formats — two-stage extract→guard, dev-scoped parsers, honest binary-layer limits 2026-07-06 10:18:43 +02:00
ba7fc68c05 docs(plan): re-sequence v0.2+ streams — defer consumer integration, add OKF inbox showcase as next build
Ground-truth pass over the named flagship consumer (portfolio-optimiser's
'OKF-upload-inbox') found it does not exist as a seam: both optimiser siblings
are frozen at release, carry their own OKF layer, receive no external bundles,
and take no dependency on this guard. Defer consumer integration (stream 2)
until the guard is mature; mature it here first, Node-port-friendly. Next
concrete build is the in-repo OKF inbox showcase (mode-b import_bundle as a
receive/quarantine gate), spec'd under 'The OKF inbox showcase'.
2026-07-06 10:12:50 +02:00
43368e9684 docs(plan): lock v0.2+ stream sequencing (OKF->consumers->Node) + dependency rationale 2026-07-06 07:30:09 +02:00
5cd9b451fb docs(okf): capture OKF-ingestion security brief as v0.2 future work
Google's Open Knowledge Format (OKF v0.1) is the LLM-wiki pattern this
library guards. Capture a security brief for OKF ingestion as tracked
future work -- NOT shipped scope. The v0.1 core is format-agnostic text;
OKF surfaces beyond the body (YAML frontmatter, resource URLs, cross-link
graph, file path / reserved names, format-level authenticity) are uncovered.

- Coverage claims verified against the code at 5397ba1 (brief section 9): no
  YAML parse in src, neutralize defangs but has no reject-gate, no path/graph
  logic, disposition machinery exists.
- OKF confirmed real at the format level (Google Cloud, 2026-06-12; spec
  GoogleCloudPlatform/knowledge-catalog).
- Design move: an OKF adapter ON TOP of the format-agnostic core; the core
  stays text->findings.
- Section 8 lists 8 v0.2 tasks (T1-T8); T8 (surface two OKF residual risks in
  README honest-limits) can land independently.
2026-07-06 07:07:09 +02:00
a55404460a docs(plan): add end-to-end showcase pipeline test as the final deliverable
One realistic content sample carrying many vulnerabilities at once, run through
a mock ingestion pipeline (sanitize -> lexicon+entropy+decode-rescan -> output
gate -> disposition); assert every planted vuln is caught and disposition fails
secure. Doubles as the README worked example. Added to build-order step 11 and
the test strategy; tracked as task #11. Inspiration: llm-security/examples/*.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K8GmKRCdsPjWYAKWsNgeQS
2026-07-04 17:23:17 +02:00
a9c4ccd8c7 feat: scaffold package + report and sanitize modules (TDD)
Build order steps 1-2 of docs/PLAN.md:
- pyproject.toml (llm-ingestion-guard, stdlib-only core, extras [ml]/[judge]/[dev]), LICENSE (MIT)
- report: Finding/Report/Severity/Source shared type (pure data)
- sanitize: carrier stripping (zero-width, BIDI, Unicode-tag, HTML comment,
  data: URI) with the byte-identical / removes-only invariant
- docs/PLAN.md: v1 implementation plan (positioning A, gap-expanded scope,
  llm-security reuse map)

15 tests passing.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K8GmKRCdsPjWYAKWsNgeQS
2026-07-04 09:24:20 +02:00
d983aa3c95 docs(brief): fold in ms-ai-architect Layer B learnings (first output-side impl)
Based on the working, tested Layer B ingestion-gate in ms-ai-architect (2026-07-04):
- A: correct consumer 2 — it fetches authored Learn docs + code samples via the
  microsoft-learn MCP, NOT the open Q&A forum / MSDN / Stack Overflow (that claim
  was unverified/overstated); low-trust surface is intra-document. Repo/name
  reconciled: the consumer is ms-ai-architect, no separate "MS AI Security plugin".
- B: §4.7 — trust tiers WITHIN a document (code sample / localized string vs
  authored prose), not only across sources; carriers + critical block in any tier.
- C: §12 — consume the lexicon as pure imported functions, not the llm-security
  scan CLI (which under-covers Markdown prose + base64-in-code-block, verified).
- D: §4.6 — fail-secure extends to scanner-unavailable: un-scannable ⇒ BLOCK.
Adds ms-ai-architect as the second reference implementation (output side).
2026-07-04 08:32:38 +02:00
41473d9db2 docs(brief): add MS AI Security plugin as second consumer; source-trust disposition
Second target consumer named: MS AI Security plugin, an ingestion
pipeline over Microsoft Learn content that includes user-generated Q&A
(learn.microsoft.com/answers, plus ingested MSDN/Stack Overflow). This
is the high-untrust case where the contract is load-bearing, not
hygiene. New design principle 4.7: disposition scales with source trust
(pinned changelog = WARN; open UGC = quarantine/hard-fail on high
severity). Day-1 rationale: architectural controls are cheap to design
in, expensive to retrofit (consumer 1 is proving that at its A13).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HPAmFyEVWbwvmSNVdXTu4d
2026-07-04 06:26:55 +02:00
b505f70485 docs: design brief for reusable LLM ingestion-pipeline security library
Brief-stage repo. docs/BRIEF.md defines a minimal, framework-agnostic
library that packages the write-time ingestion contract (sanitize ->
fence -> tool-less quarantined transform -> per-stage capability
isolation -> scan output before persist -> fail-secure) as reusable
code. Positioned honestly against query-time guardrails (LLM Guard,
NeMo, Rebuff, Vigil) with a prior-art verification log. Reference
implementation: claude-code-llm-wiki Stage B.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HPAmFyEVWbwvmSNVdXTu4d
2026-07-04 06:18:32 +02:00