limes-guard 0.17.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- limes_guard-0.17.1/.github/ISSUE_TEMPLATE/bug_report.md +40 -0
- limes_guard-0.17.1/.github/ISSUE_TEMPLATE/feature_request.md +30 -0
- limes_guard-0.17.1/.github/PULL_REQUEST_TEMPLATE.md +20 -0
- limes_guard-0.17.1/.github/workflows/ci.yml +60 -0
- limes_guard-0.17.1/.github/workflows/release.yml +109 -0
- limes_guard-0.17.1/.gitignore +18 -0
- limes_guard-0.17.1/.gitleaks.toml +42 -0
- limes_guard-0.17.1/.pre-commit-config.yaml +28 -0
- limes_guard-0.17.1/.python-version +1 -0
- limes_guard-0.17.1/CHANGELOG.md +1180 -0
- limes_guard-0.17.1/CLA.md +139 -0
- limes_guard-0.17.1/CODE_OF_CONDUCT.md +129 -0
- limes_guard-0.17.1/CONTRIBUTING.md +99 -0
- limes_guard-0.17.1/LICENSE +201 -0
- limes_guard-0.17.1/Makefile +46 -0
- limes_guard-0.17.1/PKG-INFO +1397 -0
- limes_guard-0.17.1/README.md +1364 -0
- limes_guard-0.17.1/SECURITY.md +69 -0
- limes_guard-0.17.1/docs/audit-trail.md +282 -0
- limes_guard-0.17.1/docs/decisions/0001-record-architecture-decisions.md +37 -0
- limes_guard-0.17.1/docs/decisions/0002-a-verdict-carries-its-evidence.md +80 -0
- limes_guard-0.17.1/docs/decisions/0003-no-detector-without-eval-and-null-control.md +72 -0
- limes_guard-0.17.1/docs/decisions/0004-core-detectors-transports.md +72 -0
- limes_guard-0.17.1/docs/decisions/0005-mcp-proxy-transport.md +183 -0
- limes_guard-0.17.1/docs/decisions/0006-egress-redaction.md +142 -0
- limes_guard-0.17.1/docs/decisions/0007-mcp-streamable-http-transport.md +99 -0
- limes_guard-0.17.1/docs/decisions/0008-mask-styles.md +92 -0
- limes_guard-0.17.1/docs/decisions/0009-egress-corpus-synthetic-only.md +85 -0
- limes_guard-0.17.1/docs/decisions/0010-vendor-key-vectors-are-stored-assembled.md +85 -0
- limes_guard-0.17.1/docs/decisions/0011-a-crash-is-not-a-verdict.md +114 -0
- limes_guard-0.17.1/docs/decisions/0012-the-egress-leg-scans-for-injection.md +89 -0
- limes_guard-0.17.1/docs/decisions/0013-a-classifier-layer-enters-by-the-same-gate.md +67 -0
- limes_guard-0.17.1/docs/decisions/0014-the-package-names-its-own-version.md +116 -0
- limes_guard-0.17.1/docs/decisions/0015-a-ratchet-reports-its-own-blind-spot.md +102 -0
- limes_guard-0.17.1/docs/decisions/0016-apache-2-throughout.md +70 -0
- limes_guard-0.17.1/docs/decisions/0017-an-adversary-we-did-not-write.md +148 -0
- limes_guard-0.17.1/docs/decisions/0018-the-leg-selects-its-detectors.md +113 -0
- limes_guard-0.17.1/docs/decisions/0019-one-override-family-for-both-legs.md +74 -0
- limes_guard-0.17.1/docs/decisions/0020-a-comparison-on-ground-nobody-here-owns.md +258 -0
- limes_guard-0.17.1/docs/decisions/0021-the-outbound-leg-meets-an-adversary-too.md +416 -0
- limes_guard-0.17.1/docs/decisions/0022-an-unprefixed-secret-is-caught-by-its-name-or-not-at-all.md +225 -0
- limes_guard-0.17.1/docs/decisions/0023-a-scan-is-bounded-in-time-not-only-in-size.md +106 -0
- limes_guard-0.17.1/docs/decisions/0024-the-relay-owns-every-tool-call-and-its-own-annotation.md +97 -0
- limes_guard-0.17.1/docs/decisions/0025-the-chain-seals-the-disposition-and-the-witness-names-its-build.md +74 -0
- limes_guard-0.17.1/docs/decisions/0026-a-blind-spot-names-its-cause.md +118 -0
- limes_guard-0.17.1/docs/decisions/0027-a-witness-names-its-leg.md +76 -0
- limes_guard-0.17.1/docs/decisions/0028-the-guard-says-what-it-cannot-read.md +169 -0
- limes_guard-0.17.1/docs/decisions/0029-a-declared-limit-names-its-family.md +157 -0
- limes_guard-0.17.1/docs/decisions/0030-the-outbound-leg-meets-its-competitors.md +251 -0
- limes_guard-0.17.1/docs/decisions/0031-the-distribution-takes-the-name-the-index-allows.md +82 -0
- limes_guard-0.17.1/docs/design/mcp-proxy-v0.2.md +134 -0
- limes_guard-0.17.1/docs/guarding-an-mcp-server.md +157 -0
- limes_guard-0.17.1/docs/images/how-it-works.png +0 -0
- limes_guard-0.17.1/docs/images/how-it-works.svg +69 -0
- limes_guard-0.17.1/docs/measuring-detection.md +143 -0
- limes_guard-0.17.1/docs/threat-model.md +172 -0
- limes_guard-0.17.1/docs/writing-a-detector.md +161 -0
- limes_guard-0.17.1/eval/corpus/garak/PROVENANCE.md +116 -0
- limes_guard-0.17.1/eval/corpus/garak/latent_injection.json +6160 -0
- limes_guard-0.17.1/eval/corpus/garak/latent_jailbreak.json +1043 -0
- limes_guard-0.17.1/eval/corpus/garak/prompt_hijack.json +349 -0
- limes_guard-0.17.1/eval/corpus/gitleaks/PROVENANCE.md +124 -0
- limes_guard-0.17.1/eval/corpus/gitleaks/secret_formats.json +8979 -0
- limes_guard-0.17.1/eval/corpus/presidio/PROVENANCE.md +146 -0
- limes_guard-0.17.1/eval/corpus/presidio/pii_templates.json +417 -0
- limes_guard-0.17.1/eval/matrices/comparison/PROVENANCE.md +245 -0
- limes_guard-0.17.1/eval/matrices/comparison/deberta-injection-v2.results.json +4516 -0
- limes_guard-0.17.1/eval/matrices/comparison/deepset-injection.results.json +4516 -0
- limes_guard-0.17.1/eval/matrices/comparison/llm-guard.results.json +6007 -0
- limes_guard-0.17.1/eval/matrices/comparison/mcp-firewall.results.json +4511 -0
- limes_guard-0.17.1/eval/matrices/comparison/pii/mcp-firewall.results.json +2199 -0
- limes_guard-0.17.1/eval/matrices/comparison/pii/presidio.results.json +7506 -0
- limes_guard-0.17.1/eval/matrices/comparison/rebuff.results.json +1530 -0
- limes_guard-0.17.1/eval/matrices/comparison/secrets/detect-secrets.results.json +27391 -0
- limes_guard-0.17.1/eval/matrices/comparison/secrets/gitleaks.results.json +2318 -0
- limes_guard-0.17.1/eval/matrices/comparison/secrets/mcp-firewall.results.json +1095 -0
- limes_guard-0.17.1/eval/matrices/comparison.md +218 -0
- limes_guard-0.17.1/eval/matrices/comparison_pii.md +84 -0
- limes_guard-0.17.1/eval/matrices/comparison_secrets.md +113 -0
- limes_guard-0.17.1/eval/matrices/external_injection.md +74 -0
- limes_guard-0.17.1/eval/matrices/external_pii.md +54 -0
- limes_guard-0.17.1/eval/matrices/external_secrets.md +52 -0
- limes_guard-0.17.1/eval/matrices/injection.md +29 -0
- limes_guard-0.17.1/eval/matrices/injection_egress.md +56 -0
- limes_guard-0.17.1/eval/matrices/pii_egress.md +46 -0
- limes_guard-0.17.1/eval/matrices/secrets_egress.md +43 -0
- limes_guard-0.17.1/pyproject.toml +191 -0
- limes_guard-0.17.1/scripts/bench_competitors.py +1506 -0
- limes_guard-0.17.1/scripts/gate.sh +45 -0
- limes_guard-0.17.1/scripts/vendor_garak_corpus.py +438 -0
- limes_guard-0.17.1/scripts/vendor_gitleaks_corpus.py +397 -0
- limes_guard-0.17.1/scripts/vendor_presidio_corpus.py +279 -0
- limes_guard-0.17.1/src/limes/__init__.py +103 -0
- limes_guard-0.17.1/src/limes/baselines/__init__.py +12 -0
- limes_guard-0.17.1/src/limes/baselines/tessera_pii.py +125 -0
- limes_guard-0.17.1/src/limes/baselines/tessera_regex.py +48 -0
- limes_guard-0.17.1/src/limes/cli.py +300 -0
- limes_guard-0.17.1/src/limes/corpus/PROVENANCE.md +28 -0
- limes_guard-0.17.1/src/limes/corpus/benign/benign_inputs.json +10 -0
- limes_guard-0.17.1/src/limes/corpus/egress/_schema.json +104 -0
- limes_guard-0.17.1/src/limes/corpus/egress/injection_benign.json +147 -0
- limes_guard-0.17.1/src/limes/corpus/egress/injection_positive.json +135 -0
- limes_guard-0.17.1/src/limes/corpus/egress/pii_benign.json +189 -0
- limes_guard-0.17.1/src/limes/corpus/egress/pii_positive.json +287 -0
- limes_guard-0.17.1/src/limes/corpus/egress/secrets_benign.json +245 -0
- limes_guard-0.17.1/src/limes/corpus/egress/secrets_positive.json +247 -0
- limes_guard-0.17.1/src/limes/corpus/injection/01_prompt_injection.json +35 -0
- limes_guard-0.17.1/src/limes/corpus/injection/06_prompt_injection_virement.json +37 -0
- limes_guard-0.17.1/src/limes/corpus/injection/07_prompt_injection_roleplay.json +31 -0
- limes_guard-0.17.1/src/limes/corpus/injection/08_prompt_injection_beneficiaire.json +37 -0
- limes_guard-0.17.1/src/limes/corpus/injection/09_prompt_injection_multilingue.json +28 -0
- limes_guard-0.17.1/src/limes/corpus/injection/10_prompt_injection_motif.json +30 -0
- limes_guard-0.17.1/src/limes/corpus/injection/11_prompt_injection_base64.json +29 -0
- limes_guard-0.17.1/src/limes/corpus/injection/41_prompt_injection_rag_poison.json +45 -0
- limes_guard-0.17.1/src/limes/corpus/injection/42_prompt_injection_email_zeroclick.json +46 -0
- limes_guard-0.17.1/src/limes/corpus/injection/43_prompt_injection_binding_offer.json +44 -0
- limes_guard-0.17.1/src/limes/corpus/injection/44_prompt_injection_system_prompt_leak.json +46 -0
- limes_guard-0.17.1/src/limes/corpus/injection/_schema.json +125 -0
- limes_guard-0.17.1/src/limes/detector.py +145 -0
- limes_guard-0.17.1/src/limes/detectors/__init__.py +37 -0
- limes_guard-0.17.1/src/limes/detectors/checksums.py +192 -0
- limes_guard-0.17.1/src/limes/detectors/egress.yaml +388 -0
- limes_guard-0.17.1/src/limes/detectors/egress_policy.py +373 -0
- limes_guard-0.17.1/src/limes/detectors/egress_scan.py +154 -0
- limes_guard-0.17.1/src/limes/detectors/injection.py +93 -0
- limes_guard-0.17.1/src/limes/detectors/injection_egress.py +96 -0
- limes_guard-0.17.1/src/limes/detectors/pii_egress.py +86 -0
- limes_guard-0.17.1/src/limes/detectors/policy.yaml +175 -0
- limes_guard-0.17.1/src/limes/detectors/readability.py +285 -0
- limes_guard-0.17.1/src/limes/detectors/secrets_egress.py +104 -0
- limes_guard-0.17.1/src/limes/eval/__init__.py +10 -0
- limes_guard-0.17.1/src/limes/eval/comparison_harness.py +1482 -0
- limes_guard-0.17.1/src/limes/eval/corpus.py +66 -0
- limes_guard-0.17.1/src/limes/eval/egress_comparison_harness.py +1319 -0
- limes_guard-0.17.1/src/limes/eval/egress_corpus.py +267 -0
- limes_guard-0.17.1/src/limes/eval/egress_harness.py +503 -0
- limes_guard-0.17.1/src/limes/eval/external_corpus.py +229 -0
- limes_guard-0.17.1/src/limes/eval/external_egress_corpus.py +358 -0
- limes_guard-0.17.1/src/limes/eval/external_egress_harness.py +591 -0
- limes_guard-0.17.1/src/limes/eval/external_harness.py +442 -0
- limes_guard-0.17.1/src/limes/eval/harness.py +260 -0
- limes_guard-0.17.1/src/limes/eval/power.py +98 -0
- limes_guard-0.17.1/src/limes/guard.py +193 -0
- limes_guard-0.17.1/src/limes/policy.py +195 -0
- limes_guard-0.17.1/src/limes/py.typed +0 -0
- limes_guard-0.17.1/src/limes/record.py +166 -0
- limes_guard-0.17.1/src/limes/registry.py +28 -0
- limes_guard-0.17.1/src/limes/spans.py +57 -0
- limes_guard-0.17.1/src/limes/transports/__init__.py +25 -0
- limes_guard-0.17.1/src/limes/transports/in_process.py +355 -0
- limes_guard-0.17.1/src/limes/transports/mcp/__init__.py +81 -0
- limes_guard-0.17.1/src/limes/transports/mcp/__main__.py +12 -0
- limes_guard-0.17.1/src/limes/transports/mcp/bench.py +175 -0
- limes_guard-0.17.1/src/limes/transports/mcp/bench_http.py +214 -0
- limes_guard-0.17.1/src/limes/transports/mcp/bridge.py +1065 -0
- limes_guard-0.17.1/src/limes/transports/mcp/cli.py +263 -0
- limes_guard-0.17.1/src/limes/transports/mcp/config.py +276 -0
- limes_guard-0.17.1/src/limes/transports/mcp/decision.py +258 -0
- limes_guard-0.17.1/src/limes/transports/mcp/http.py +501 -0
- limes_guard-0.17.1/src/limes/transports/mcp/payload.py +305 -0
- limes_guard-0.17.1/src/limes/transports/mcp/sink.py +129 -0
- limes_guard-0.17.1/src/limes/transports/redaction.py +610 -0
- limes_guard-0.17.1/src/limes/verdict.py +300 -0
- limes_guard-0.17.1/tests/__init__.py +0 -0
- limes_guard-0.17.1/tests/conftest.py +21 -0
- limes_guard-0.17.1/tests/integration/__init__.py +0 -0
- limes_guard-0.17.1/tests/integration/egress/__init__.py +0 -0
- limes_guard-0.17.1/tests/integration/egress/guarded_http_proxy.py +32 -0
- limes_guard-0.17.1/tests/integration/egress/guarded_proxy.py +45 -0
- limes_guard-0.17.1/tests/integration/egress/test_pii_egress_e2e.py +324 -0
- limes_guard-0.17.1/tests/integration/mcp/__init__.py +0 -0
- limes_guard-0.17.1/tests/integration/mcp/http_journal_server.py +89 -0
- limes_guard-0.17.1/tests/integration/mcp/http_proxy.py +28 -0
- limes_guard-0.17.1/tests/integration/mcp/journal_server.py +81 -0
- limes_guard-0.17.1/tests/integration/mcp/poisoned_server.py +88 -0
- limes_guard-0.17.1/tests/integration/mcp/redacting_http_proxy.py +44 -0
- limes_guard-0.17.1/tests/integration/mcp/redacting_proxy.py +38 -0
- limes_guard-0.17.1/tests/integration/mcp/test_end_to_end.py +220 -0
- limes_guard-0.17.1/tests/integration/mcp/test_http_e2e.py +416 -0
- limes_guard-0.17.1/tests/integration/mcp/test_redaction_e2e.py +212 -0
- limes_guard-0.17.1/tests/integration/mcp/test_tools_list_poisoning_e2e.py +132 -0
- limes_guard-0.17.1/tests/integration/test_evidence_chain_replay.py +44 -0
- limes_guard-0.17.1/tests/unit/__init__.py +0 -0
- limes_guard-0.17.1/tests/unit/cli/__init__.py +0 -0
- limes_guard-0.17.1/tests/unit/cli/test_check.py +280 -0
- limes_guard-0.17.1/tests/unit/cli/test_leg_selects_detectors.py +74 -0
- limes_guard-0.17.1/tests/unit/comparison/test_comparison_harness.py +359 -0
- limes_guard-0.17.1/tests/unit/comparison/test_egress_comparison_harness.py +445 -0
- limes_guard-0.17.1/tests/unit/egress/__init__.py +0 -0
- limes_guard-0.17.1/tests/unit/egress/test_aws_secret_context_rule.py +264 -0
- limes_guard-0.17.1/tests/unit/egress/test_checksums.py +164 -0
- limes_guard-0.17.1/tests/unit/egress/test_corpus_is_synthetic.py +187 -0
- limes_guard-0.17.1/tests/unit/egress/test_egress_policy.py +185 -0
- limes_guard-0.17.1/tests/unit/egress/test_external_egress_corpus.py +153 -0
- limes_guard-0.17.1/tests/unit/egress/test_grader_integrity.py +112 -0
- limes_guard-0.17.1/tests/unit/egress/test_no_raw_secret_literal_in_corpus_file.py +94 -0
- limes_guard-0.17.1/tests/unit/egress/test_no_raw_value_in_records.py +120 -0
- limes_guard-0.17.1/tests/unit/egress/test_pii_detector.py +201 -0
- limes_guard-0.17.1/tests/unit/egress/test_precision_gates_are_load_bearing.py +203 -0
- limes_guard-0.17.1/tests/unit/egress/test_secrets_detector.py +218 -0
- limes_guard-0.17.1/tests/unit/external/test_external_corpus.py +143 -0
- limes_guard-0.17.1/tests/unit/external/test_external_harness.py +55 -0
- limes_guard-0.17.1/tests/unit/mcp/__init__.py +0 -0
- limes_guard-0.17.1/tests/unit/mcp/harness.py +258 -0
- limes_guard-0.17.1/tests/unit/mcp/test_cli.py +143 -0
- limes_guard-0.17.1/tests/unit/mcp/test_decision.py +107 -0
- limes_guard-0.17.1/tests/unit/mcp/test_http.py +104 -0
- limes_guard-0.17.1/tests/unit/mcp/test_outbound_leg_is_wired.py +74 -0
- limes_guard-0.17.1/tests/unit/mcp/test_payload.py +52 -0
- limes_guard-0.17.1/tests/unit/mcp/test_redaction.py +283 -0
- limes_guard-0.17.1/tests/unit/mcp/test_relay.py +787 -0
- limes_guard-0.17.1/tests/unit/mcp/test_sink.py +122 -0
- limes_guard-0.17.1/tests/unit/ratchets/__init__.py +0 -0
- limes_guard-0.17.1/tests/unit/ratchets/test_a_blind_spot_names_its_cause.py +239 -0
- limes_guard-0.17.1/tests/unit/ratchets/test_a_crash_is_not_a_verdict.py +54 -0
- limes_guard-0.17.1/tests/unit/ratchets/test_a_declared_limit_names_its_family.py +557 -0
- limes_guard-0.17.1/tests/unit/ratchets/test_a_witness_names_its_leg.py +137 -0
- limes_guard-0.17.1/tests/unit/ratchets/test_allow_needs_evidence_mypy.py +69 -0
- limes_guard-0.17.1/tests/unit/ratchets/test_disposition_is_sealed.py +80 -0
- limes_guard-0.17.1/tests/unit/ratchets/test_exceptions_frozen_at_zero.py +42 -0
- limes_guard-0.17.1/tests/unit/ratchets/test_guard_refuses_unknown.py +49 -0
- limes_guard-0.17.1/tests/unit/ratchets/test_inbound_scan_is_budgeted.py +117 -0
- limes_guard-0.17.1/tests/unit/ratchets/test_null_result_carries_power.py +43 -0
- limes_guard-0.17.1/tests/unit/ratchets/test_scan_cost_is_bounded.py +101 -0
- limes_guard-0.17.1/tests/unit/ratchets/test_span_digest_is_total.py +61 -0
- limes_guard-0.17.1/tests/unit/ratchets/test_the_guard_says_what_it_cannot_read.py +420 -0
- limes_guard-0.17.1/tests/unit/ratchets/test_witness_names_its_build.py +39 -0
- limes_guard-0.17.1/tests/unit/redaction/__init__.py +0 -0
- limes_guard-0.17.1/tests/unit/redaction/doubles.py +109 -0
- limes_guard-0.17.1/tests/unit/redaction/test_in_process.py +161 -0
- limes_guard-0.17.1/tests/unit/redaction/test_plan.py +147 -0
- limes_guard-0.17.1/tests/unit/redaction/test_policy.py +128 -0
- limes_guard-0.17.1/tests/unit/redaction/test_style.py +243 -0
- limes_guard-0.17.1/tests/unit/test_admission_rule.py +127 -0
- limes_guard-0.17.1/tests/unit/test_corpus_provenance.py +55 -0
- limes_guard-0.17.1/tests/unit/test_detector_protocol.py +70 -0
- limes_guard-0.17.1/tests/unit/test_frontier.py +953 -0
- limes_guard-0.17.1/tests/unit/test_hijack_family_names_its_templates.py +80 -0
- limes_guard-0.17.1/tests/unit/test_injection_detector.py +108 -0
- limes_guard-0.17.1/tests/unit/test_matrices_match_their_harnesses.py +120 -0
- limes_guard-0.17.1/tests/unit/test_power.py +69 -0
- limes_guard-0.17.1/tests/unit/test_record_chain.py +54 -0
- limes_guard-0.17.1/tests/unit/test_verdict.py +137 -0
- limes_guard-0.17.1/uv.lock +989 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Bug report
|
|
3
|
+
about: A defect in the limes engine, a detector, or a transport
|
|
4
|
+
title: "bug: "
|
|
5
|
+
labels: bug
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
**Do not use this for security vulnerabilities.** See
|
|
9
|
+
[SECURITY.md](../../SECURITY.md) for private reporting.
|
|
10
|
+
|
|
11
|
+
## What happened
|
|
12
|
+
|
|
13
|
+
A clear description of the bug.
|
|
14
|
+
|
|
15
|
+
## Reproduce
|
|
16
|
+
|
|
17
|
+
Minimal steps or a code snippet. If it involves a detector verdict, include the
|
|
18
|
+
input and the `Verdict` / `Evidence` you observed.
|
|
19
|
+
|
|
20
|
+
```python
|
|
21
|
+
# minimal repro
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Expected
|
|
25
|
+
|
|
26
|
+
What you expected instead (e.g. `Deny` with evidence, `CannotSay`, `Allow`).
|
|
27
|
+
|
|
28
|
+
## Environment
|
|
29
|
+
|
|
30
|
+
- limes version / commit:
|
|
31
|
+
- Python version:
|
|
32
|
+
- OS:
|
|
33
|
+
|
|
34
|
+
## Which layer (best guess)
|
|
35
|
+
|
|
36
|
+
- [ ] Core (verdict algebra, evidence chain)
|
|
37
|
+
- [ ] Detector / policy
|
|
38
|
+
- [ ] Transport
|
|
39
|
+
- [ ] Eval harness
|
|
40
|
+
- [ ] Docs
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Feature request
|
|
3
|
+
about: Propose a new detector, policy, or transport (the core never grows)
|
|
4
|
+
title: "feat: "
|
|
5
|
+
labels: enhancement
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## What and why
|
|
9
|
+
|
|
10
|
+
What capability is missing, and the problem it solves.
|
|
11
|
+
|
|
12
|
+
## Which layer (ADR 0004)
|
|
13
|
+
|
|
14
|
+
A new capability is a plugin, never a change to the core.
|
|
15
|
+
|
|
16
|
+
- [ ] Detector (new detection capability)
|
|
17
|
+
- [ ] Policy (new rules for an existing detector)
|
|
18
|
+
- [ ] Transport (new integration surface — MCP, HTTP, …)
|
|
19
|
+
- [ ] Something that seems to need a core change → it needs a new ADR; say why
|
|
20
|
+
|
|
21
|
+
## If this is a detector: the admission rule (ADR 0003)
|
|
22
|
+
|
|
23
|
+
A detector ships only with its **two numbers**. Sketch how you would measure it:
|
|
24
|
+
|
|
25
|
+
- **Positive corpus** — the attacks it catches:
|
|
26
|
+
- **Benign corpus** — legitimate traffic it must not kill:
|
|
27
|
+
- **Null control** — how it beats doing nothing, and how it avoids block-everything:
|
|
28
|
+
|
|
29
|
+
A detector without a benign corpus and a null control will not be admitted,
|
|
30
|
+
however easy it looks.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
## What this changes
|
|
2
|
+
|
|
3
|
+
A short description, and the motivation. Link any issue or ADR.
|
|
4
|
+
|
|
5
|
+
## Checklist
|
|
6
|
+
|
|
7
|
+
- [ ] `make gate` is green locally (ruff + ruff format --check + mypy --strict + pytest).
|
|
8
|
+
- [ ] Commits follow **Conventional Commits** (`feat:`, `fix:`, `docs:`, …).
|
|
9
|
+
- [ ] Structural change? It references or adds an ADR under `docs/decisions/`.
|
|
10
|
+
- [ ] I have signed the [CLA](CLA.md) (required before a first merge).
|
|
11
|
+
|
|
12
|
+
## If this adds or changes a detector (ADR 0003)
|
|
13
|
+
|
|
14
|
+
- [ ] Positive corpus added / updated.
|
|
15
|
+
- [ ] Benign corpus added / updated.
|
|
16
|
+
- [ ] Null control measured (vs unplugged **and** vs block-everything).
|
|
17
|
+
- [ ] Confusion matrix regenerated (`make eval`) and committed under `eval/matrices/`.
|
|
18
|
+
- [ ] The two numbers are in the PR description below.
|
|
19
|
+
|
|
20
|
+
**The two numbers:** attacks blocked = `…`, legitimate traffic killed = `…`.
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
name: ci
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
|
|
12
|
+
concurrency:
|
|
13
|
+
group: ci-${{ github.ref }}
|
|
14
|
+
cancel-in-progress: true
|
|
15
|
+
|
|
16
|
+
jobs:
|
|
17
|
+
gate:
|
|
18
|
+
name: gate (ruff · mypy --strict · pytest) — py${{ matrix.python-version }}
|
|
19
|
+
runs-on: ubuntu-latest
|
|
20
|
+
strategy:
|
|
21
|
+
# Every supported Python reports for itself. One red must not hide the others.
|
|
22
|
+
fail-fast: false
|
|
23
|
+
matrix:
|
|
24
|
+
# These are exactly the `Programming Language :: Python` classifiers in
|
|
25
|
+
# pyproject.toml, and `requires-python = ">=3.12"` is the claim they witness.
|
|
26
|
+
# A version claimed and never run is a promise with no witness.
|
|
27
|
+
python-version: ["3.12", "3.13", "3.14"]
|
|
28
|
+
env:
|
|
29
|
+
# uv reads this for both `uv sync` and the `uv run` calls inside make gate,
|
|
30
|
+
# so the whole gate runs on the matrix version rather than on .python-version.
|
|
31
|
+
UV_PYTHON: ${{ matrix.python-version }}
|
|
32
|
+
steps:
|
|
33
|
+
- uses: actions/checkout@v4
|
|
34
|
+
with:
|
|
35
|
+
# NOT the default depth 1. tests/unit/test_frontier.py compares today's
|
|
36
|
+
# bytes against the v0.1 commit, read out of git history — and a shallow
|
|
37
|
+
# checkout cannot produce it. With the default, that ratchet failed on
|
|
38
|
+
# every push from v0.1 to v0.8: 16 red runs, one green, on v0.1 itself
|
|
39
|
+
# where the comparison was trivially true. The gate was green locally the
|
|
40
|
+
# whole time, because a normal clone has the history.
|
|
41
|
+
fetch-depth: 0
|
|
42
|
+
|
|
43
|
+
- name: Install uv
|
|
44
|
+
uses: astral-sh/setup-uv@v5
|
|
45
|
+
with:
|
|
46
|
+
enable-cache: true
|
|
47
|
+
|
|
48
|
+
- name: Install the matrix Python
|
|
49
|
+
run: uv python install ${{ matrix.python-version }}
|
|
50
|
+
|
|
51
|
+
- name: Sync dependencies
|
|
52
|
+
run: uv sync
|
|
53
|
+
|
|
54
|
+
- name: Say which tree and which Python this gate judges
|
|
55
|
+
run: |
|
|
56
|
+
uv run python -VV
|
|
57
|
+
make tree
|
|
58
|
+
|
|
59
|
+
- name: Gate — ruff + ruff format --check + mypy --strict + pytest
|
|
60
|
+
run: make gate
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
name: release
|
|
2
|
+
|
|
3
|
+
# Publishing is deliberate: a tag, or a hand-pulled lever. Never a push to main.
|
|
4
|
+
on:
|
|
5
|
+
push:
|
|
6
|
+
tags: ["v*"]
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
inputs:
|
|
9
|
+
target:
|
|
10
|
+
description: "where to publish"
|
|
11
|
+
required: true
|
|
12
|
+
default: testpypi
|
|
13
|
+
type: choice
|
|
14
|
+
options: [testpypi, pypi]
|
|
15
|
+
|
|
16
|
+
permissions:
|
|
17
|
+
contents: read
|
|
18
|
+
|
|
19
|
+
jobs:
|
|
20
|
+
# The gate runs again here, on the exact tag, rather than trusting that the
|
|
21
|
+
# commit was green when it landed: a tag can be moved, and the thing published
|
|
22
|
+
# must be the thing judged (ADR 0026 — a witness interrogates the thing).
|
|
23
|
+
gate:
|
|
24
|
+
name: gate before publishing — py${{ matrix.python-version }}
|
|
25
|
+
runs-on: ubuntu-latest
|
|
26
|
+
strategy:
|
|
27
|
+
fail-fast: false
|
|
28
|
+
matrix:
|
|
29
|
+
python-version: ["3.12", "3.13", "3.14"]
|
|
30
|
+
env:
|
|
31
|
+
UV_PYTHON: ${{ matrix.python-version }}
|
|
32
|
+
steps:
|
|
33
|
+
- uses: actions/checkout@v4
|
|
34
|
+
with:
|
|
35
|
+
fetch-depth: 0 # the frontier ratchet reads v0.1's bytes from history
|
|
36
|
+
- uses: astral-sh/setup-uv@v5
|
|
37
|
+
with:
|
|
38
|
+
enable-cache: true
|
|
39
|
+
- run: uv python install ${{ matrix.python-version }}
|
|
40
|
+
- run: uv sync
|
|
41
|
+
- run: make gate
|
|
42
|
+
|
|
43
|
+
build:
|
|
44
|
+
name: build sdist + wheel
|
|
45
|
+
needs: gate
|
|
46
|
+
runs-on: ubuntu-latest
|
|
47
|
+
steps:
|
|
48
|
+
- uses: actions/checkout@v4
|
|
49
|
+
with:
|
|
50
|
+
fetch-depth: 0
|
|
51
|
+
- uses: astral-sh/setup-uv@v5
|
|
52
|
+
|
|
53
|
+
- name: Build
|
|
54
|
+
run: uv build
|
|
55
|
+
|
|
56
|
+
- name: The tag and the version must be the same number
|
|
57
|
+
if: startsWith(github.ref, 'refs/tags/v')
|
|
58
|
+
run: |
|
|
59
|
+
set -euo pipefail
|
|
60
|
+
tag="${GITHUB_REF_NAME#v}"
|
|
61
|
+
built="$(uv run --no-project --with tomli python -c \
|
|
62
|
+
'import tomli;print(tomli.load(open("pyproject.toml","rb"))["project"]["version"])')"
|
|
63
|
+
echo "tag=$tag pyproject=$built"
|
|
64
|
+
# A tag that names a different version than the artifact it publishes is
|
|
65
|
+
# a release nobody can trace back: the sdist would claim one number and
|
|
66
|
+
# the git history another.
|
|
67
|
+
test "$tag" = "$built"
|
|
68
|
+
|
|
69
|
+
- name: Metadata must render on PyPI
|
|
70
|
+
run: uvx twine check --strict dist/*
|
|
71
|
+
|
|
72
|
+
- uses: actions/upload-artifact@v4
|
|
73
|
+
with:
|
|
74
|
+
name: dist
|
|
75
|
+
path: dist/
|
|
76
|
+
|
|
77
|
+
publish-testpypi:
|
|
78
|
+
name: publish to TestPyPI
|
|
79
|
+
needs: build
|
|
80
|
+
if: github.event_name == 'workflow_dispatch' && inputs.target == 'testpypi'
|
|
81
|
+
runs-on: ubuntu-latest
|
|
82
|
+
environment: testpypi
|
|
83
|
+
permissions:
|
|
84
|
+
id-token: write # Trusted Publishing (OIDC): no token on any machine
|
|
85
|
+
steps:
|
|
86
|
+
- uses: actions/download-artifact@v4
|
|
87
|
+
with:
|
|
88
|
+
name: dist
|
|
89
|
+
path: dist/
|
|
90
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
91
|
+
with:
|
|
92
|
+
repository-url: https://test.pypi.org/legacy/
|
|
93
|
+
|
|
94
|
+
publish-pypi:
|
|
95
|
+
name: publish to PyPI
|
|
96
|
+
needs: build
|
|
97
|
+
if: >-
|
|
98
|
+
startsWith(github.ref, 'refs/tags/v') ||
|
|
99
|
+
(github.event_name == 'workflow_dispatch' && inputs.target == 'pypi')
|
|
100
|
+
runs-on: ubuntu-latest
|
|
101
|
+
environment: pypi
|
|
102
|
+
permissions:
|
|
103
|
+
id-token: write # Trusted Publishing (OIDC): no token on any machine
|
|
104
|
+
steps:
|
|
105
|
+
- uses: actions/download-artifact@v4
|
|
106
|
+
with:
|
|
107
|
+
name: dist
|
|
108
|
+
path: dist/
|
|
109
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.egg-info/
|
|
5
|
+
.venv/
|
|
6
|
+
dist/
|
|
7
|
+
build/
|
|
8
|
+
|
|
9
|
+
# Tooling caches
|
|
10
|
+
.mypy_cache/
|
|
11
|
+
.ruff_cache/
|
|
12
|
+
.pytest_cache/
|
|
13
|
+
|
|
14
|
+
# OS
|
|
15
|
+
.DS_Store
|
|
16
|
+
|
|
17
|
+
# Eval scratch (the published matrices under eval/matrices/*.md ARE tracked)
|
|
18
|
+
*.tmp
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# limes — secret-scanner configuration.
|
|
2
|
+
#
|
|
3
|
+
# limes is a package about credentials leaving a system, so its corpus and its
|
|
4
|
+
# tests necessarily CONTAIN credential-shaped strings: that is the material under
|
|
5
|
+
# test. A scanner reading them as leaks reports eleven findings on a repository
|
|
6
|
+
# with no secret in it, and the cost of that noise is not zero — a reader who
|
|
7
|
+
# learns to dismiss this scanner will dismiss it on the day it is right.
|
|
8
|
+
#
|
|
9
|
+
# So the exemptions are named one file at a time, with what each holds. They are
|
|
10
|
+
# NOT a wildcard over tests/: a real key pasted into a new test must still ring.
|
|
11
|
+
#
|
|
12
|
+
# ADR 0010 already removed the worst of it — vendor key vectors are stored as
|
|
13
|
+
# fragments the loader joins, so no contiguous credential literal sits in the
|
|
14
|
+
# corpus. What remains below is what could not be assembled away: PEM headers,
|
|
15
|
+
# a standard example JWT, and one obviously-fake key reused across four tests.
|
|
16
|
+
#
|
|
17
|
+
# Verify with: gitleaks detect --source . --redact -v
|
|
18
|
+
|
|
19
|
+
title = "limes"
|
|
20
|
+
|
|
21
|
+
[extend]
|
|
22
|
+
useDefault = true
|
|
23
|
+
|
|
24
|
+
[[allowlists]]
|
|
25
|
+
description = """
|
|
26
|
+
Synthetic detection material, verified: the base64 bodies decode to
|
|
27
|
+
'EXAMPLE ONLY - not a key - synthetic' and 'PLACEHOLDER - synthetic - not a key',
|
|
28
|
+
the JWT is the canonical example.com token (sub 1234567890, name Example), and
|
|
29
|
+
the API key is the eight-character literal sk-live-AB12cd34. None is or ever was
|
|
30
|
+
live. Each file is the input of a test that must find exactly these strings.
|
|
31
|
+
"""
|
|
32
|
+
paths = [
|
|
33
|
+
# The egress corpus: PEM headers and an example JWT the secrets detector must span.
|
|
34
|
+
'''^src/limes/corpus/egress/secrets_positive\.json$''',
|
|
35
|
+
# The secrets detector's own unit cases (the same PEM headers).
|
|
36
|
+
'''^tests/unit/egress/test_secrets_detector\.py$''',
|
|
37
|
+
# KEY = "sk-live-AB12cd34" — the payload the redaction tests mask and assert on.
|
|
38
|
+
'''^tests/unit/redaction/test_style\.py$''',
|
|
39
|
+
'''^tests/unit/mcp/test_redaction\.py$''',
|
|
40
|
+
'''^tests/integration/mcp/test_redaction_e2e\.py$''',
|
|
41
|
+
'''^tests/integration/mcp/test_http_e2e\.py$''',
|
|
42
|
+
]
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# limes pre-commit — the same checks make gate runs, at commit time.
|
|
2
|
+
repos:
|
|
3
|
+
- repo: local
|
|
4
|
+
hooks:
|
|
5
|
+
- id: ruff-check
|
|
6
|
+
name: ruff check
|
|
7
|
+
entry: uv run ruff check --fix
|
|
8
|
+
language: system
|
|
9
|
+
types: [python]
|
|
10
|
+
pass_filenames: false
|
|
11
|
+
- id: ruff-format
|
|
12
|
+
name: ruff format
|
|
13
|
+
entry: uv run ruff format
|
|
14
|
+
language: system
|
|
15
|
+
types: [python]
|
|
16
|
+
pass_filenames: false
|
|
17
|
+
- id: mypy
|
|
18
|
+
name: mypy --strict
|
|
19
|
+
entry: uv run mypy
|
|
20
|
+
language: system
|
|
21
|
+
types: [python]
|
|
22
|
+
pass_filenames: false
|
|
23
|
+
- id: pytest
|
|
24
|
+
name: pytest
|
|
25
|
+
entry: uv run pytest -q
|
|
26
|
+
language: system
|
|
27
|
+
pass_filenames: false
|
|
28
|
+
stages: [pre-push]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.12
|