shadowshield 0.6.1__tar.gz → 0.6.3__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.
- {shadowshield-0.6.1 → shadowshield-0.6.3}/PKG-INFO +18 -15
- {shadowshield-0.6.1 → shadowshield-0.6.3}/README.md +17 -13
- {shadowshield-0.6.1 → shadowshield-0.6.3}/docs/BENCHMARKS.md +55 -17
- {shadowshield-0.6.1 → shadowshield-0.6.3}/docs/PRODUCTION_READINESS.md +54 -16
- {shadowshield-0.6.1 → shadowshield-0.6.3}/docs/RELEASING.md +46 -16
- {shadowshield-0.6.1 → shadowshield-0.6.3}/docs/configuration.md +6 -5
- {shadowshield-0.6.1 → shadowshield-0.6.3}/docs/plugins.md +3 -1
- {shadowshield-0.6.1 → shadowshield-0.6.3}/docs/security-model.md +4 -2
- {shadowshield-0.6.1 → shadowshield-0.6.3}/pyproject.toml +11 -3
- {shadowshield-0.6.1 → shadowshield-0.6.3}/src/shadowshield/__init__.py +1 -1
- {shadowshield-0.6.1 → shadowshield-0.6.3}/src/shadowshield/_security.py +107 -20
- {shadowshield-0.6.1 → shadowshield-0.6.3}/src/shadowshield/cli.py +12 -7
- {shadowshield-0.6.1 → shadowshield-0.6.3}/src/shadowshield/config/default.yaml +4 -0
- {shadowshield-0.6.1 → shadowshield-0.6.3}/src/shadowshield/control.py +281 -45
- {shadowshield-0.6.1 → shadowshield-0.6.3}/src/shadowshield/core/config.py +6 -0
- {shadowshield-0.6.1 → shadowshield-0.6.3}/src/shadowshield/core/engine.py +49 -10
- {shadowshield-0.6.1 → shadowshield-0.6.3}/src/shadowshield/core/session.py +34 -19
- {shadowshield-0.6.1 → shadowshield-0.6.3}/src/shadowshield/detectors/anomaly.py +2 -1
- {shadowshield-0.6.1 → shadowshield-0.6.3}/src/shadowshield/detectors/base.py +2 -0
- {shadowshield-0.6.1 → shadowshield-0.6.3}/src/shadowshield/detectors/exfiltration.py +6 -0
- {shadowshield-0.6.1 → shadowshield-0.6.3}/src/shadowshield/detectors/transformer.py +95 -10
- {shadowshield-0.6.1 → shadowshield-0.6.3}/src/shadowshield/detectors/vector.py +84 -1
- shadowshield-0.6.3/src/shadowshield/eval/data/generalization_benchmark_v3.jsonl +40 -0
- {shadowshield-0.6.1 → shadowshield-0.6.3}/src/shadowshield/eval/dataset.py +7 -6
- shadowshield-0.6.3/src/shadowshield/eval/harness.py +432 -0
- {shadowshield-0.6.1 → shadowshield-0.6.3}/tests/test_cli.py +58 -0
- {shadowshield-0.6.1 → shadowshield-0.6.3}/tests/test_config.py +2 -0
- {shadowshield-0.6.1 → shadowshield-0.6.3}/tests/test_control.py +204 -0
- {shadowshield-0.6.1 → shadowshield-0.6.3}/tests/test_detectors.py +17 -0
- shadowshield-0.6.3/tests/test_eval_harness.py +240 -0
- {shadowshield-0.6.1 → shadowshield-0.6.3}/tests/test_generalization_benchmarks.py +57 -1
- shadowshield-0.6.3/tests/test_http_security.py +326 -0
- {shadowshield-0.6.1 → shadowshield-0.6.3}/tests/test_production.py +55 -0
- {shadowshield-0.6.1 → shadowshield-0.6.3}/tests/test_shield.py +49 -0
- shadowshield-0.6.3/tests/test_site_security.py +120 -0
- {shadowshield-0.6.1 → shadowshield-0.6.3}/tests/test_transformer.py +135 -0
- {shadowshield-0.6.1 → shadowshield-0.6.3}/tests/test_vector.py +145 -0
- shadowshield-0.6.3/tests/test_workflow_policy.py +114 -0
- shadowshield-0.6.1/src/shadowshield/eval/harness.py +0 -206
- shadowshield-0.6.1/tests/test_http_security.py +0 -138
- {shadowshield-0.6.1 → shadowshield-0.6.3}/.gitignore +0 -0
- {shadowshield-0.6.1 → shadowshield-0.6.3}/LICENSE +0 -0
- {shadowshield-0.6.1 → shadowshield-0.6.3}/docs/CODE_REVIEW.md +0 -0
- {shadowshield-0.6.1 → shadowshield-0.6.3}/docs/COMPARISON.md +0 -0
- {shadowshield-0.6.1 → shadowshield-0.6.3}/docs/MARKET_LANDSCAPE.md +0 -0
- {shadowshield-0.6.1 → shadowshield-0.6.3}/docs/NEXT_STEPS.md +0 -0
- {shadowshield-0.6.1 → shadowshield-0.6.3}/docs/OWASP_LLM_TOP10.md +0 -0
- {shadowshield-0.6.1 → shadowshield-0.6.3}/docs/PLAN_REVIEW.md +0 -0
- {shadowshield-0.6.1 → shadowshield-0.6.3}/docs/REPORTER_SDK_SPEC.md +0 -0
- {shadowshield-0.6.1 → shadowshield-0.6.3}/docs/SAAS_STRATEGY.md +0 -0
- {shadowshield-0.6.1 → shadowshield-0.6.3}/docs/UPGRADE_OPPORTUNITIES.md +0 -0
- {shadowshield-0.6.1 → shadowshield-0.6.3}/docs/detectors.md +0 -0
- {shadowshield-0.6.1 → shadowshield-0.6.3}/docs/index.md +0 -0
- {shadowshield-0.6.1 → shadowshield-0.6.3}/docs/research/LANDSCAPE.md +0 -0
- {shadowshield-0.6.1 → shadowshield-0.6.3}/examples/agentic_security.py +0 -0
- {shadowshield-0.6.1 → shadowshield-0.6.3}/examples/custom_detector.py +0 -0
- {shadowshield-0.6.1 → shadowshield-0.6.3}/examples/langchain_integration.py +0 -0
- {shadowshield-0.6.1 → shadowshield-0.6.3}/examples/openai_integration.py +0 -0
- {shadowshield-0.6.1 → shadowshield-0.6.3}/examples/quickstart.py +0 -0
- {shadowshield-0.6.1 → shadowshield-0.6.3}/src/shadowshield/config/__init__.py +0 -0
- {shadowshield-0.6.1 → shadowshield-0.6.3}/src/shadowshield/core/__init__.py +0 -0
- {shadowshield-0.6.1 → shadowshield-0.6.3}/src/shadowshield/core/canary.py +0 -0
- {shadowshield-0.6.1 → shadowshield-0.6.3}/src/shadowshield/core/coverage.py +0 -0
- {shadowshield-0.6.1 → shadowshield-0.6.3}/src/shadowshield/core/policy.py +0 -0
- {shadowshield-0.6.1 → shadowshield-0.6.3}/src/shadowshield/core/shield.py +0 -0
- {shadowshield-0.6.1 → shadowshield-0.6.3}/src/shadowshield/core/telemetry.py +0 -0
- {shadowshield-0.6.1 → shadowshield-0.6.3}/src/shadowshield/core/types.py +0 -0
- {shadowshield-0.6.1 → shadowshield-0.6.3}/src/shadowshield/detectors/__init__.py +0 -0
- {shadowshield-0.6.1 → shadowshield-0.6.3}/src/shadowshield/detectors/alignment.py +0 -0
- {shadowshield-0.6.1 → shadowshield-0.6.3}/src/shadowshield/detectors/canary.py +0 -0
- {shadowshield-0.6.1 → shadowshield-0.6.3}/src/shadowshield/detectors/data/attack_corpus.txt +0 -0
- {shadowshield-0.6.1 → shadowshield-0.6.3}/src/shadowshield/detectors/encoding.py +0 -0
- {shadowshield-0.6.1 → shadowshield-0.6.3}/src/shadowshield/detectors/jailbreak.py +0 -0
- {shadowshield-0.6.1 → shadowshield-0.6.3}/src/shadowshield/detectors/llm_check.py +0 -0
- {shadowshield-0.6.1 → shadowshield-0.6.3}/src/shadowshield/detectors/pii.py +0 -0
- {shadowshield-0.6.1 → shadowshield-0.6.3}/src/shadowshield/detectors/prompt_injection.py +0 -0
- {shadowshield-0.6.1 → shadowshield-0.6.3}/src/shadowshield/eval/__init__.py +0 -0
- {shadowshield-0.6.1 → shadowshield-0.6.3}/src/shadowshield/eval/data/adversarial_benchmark.jsonl +0 -0
- {shadowshield-0.6.1 → shadowshield-0.6.3}/src/shadowshield/eval/data/builtin_benchmark.jsonl +0 -0
- {shadowshield-0.6.1 → shadowshield-0.6.3}/src/shadowshield/eval/data/generalization_benchmark_v1.jsonl +0 -0
- {shadowshield-0.6.1 → shadowshield-0.6.3}/src/shadowshield/eval/data/generalization_benchmark_v2.jsonl +0 -0
- {shadowshield-0.6.1 → shadowshield-0.6.3}/src/shadowshield/integrations/__init__.py +0 -0
- {shadowshield-0.6.1 → shadowshield-0.6.3}/src/shadowshield/integrations/agentdojo.py +0 -0
- {shadowshield-0.6.1 → shadowshield-0.6.3}/src/shadowshield/integrations/mcp.py +0 -0
- {shadowshield-0.6.1 → shadowshield-0.6.3}/src/shadowshield/middleware/__init__.py +0 -0
- {shadowshield-0.6.1 → shadowshield-0.6.3}/src/shadowshield/middleware/base.py +0 -0
- {shadowshield-0.6.1 → shadowshield-0.6.3}/src/shadowshield/middleware/decorators.py +0 -0
- {shadowshield-0.6.1 → shadowshield-0.6.3}/src/shadowshield/middleware/langchain.py +0 -0
- {shadowshield-0.6.1 → shadowshield-0.6.3}/src/shadowshield/middleware/openai.py +0 -0
- {shadowshield-0.6.1 → shadowshield-0.6.3}/src/shadowshield/plugins/__init__.py +0 -0
- {shadowshield-0.6.1 → shadowshield-0.6.3}/src/shadowshield/plugins/base.py +0 -0
- {shadowshield-0.6.1 → shadowshield-0.6.3}/src/shadowshield/plugins/manager.py +0 -0
- {shadowshield-0.6.1 → shadowshield-0.6.3}/src/shadowshield/py.typed +0 -0
- {shadowshield-0.6.1 → shadowshield-0.6.3}/src/shadowshield/reporter.py +0 -0
- {shadowshield-0.6.1 → shadowshield-0.6.3}/src/shadowshield/responders/__init__.py +0 -0
- {shadowshield-0.6.1 → shadowshield-0.6.3}/src/shadowshield/responders/base.py +0 -0
- {shadowshield-0.6.1 → shadowshield-0.6.3}/src/shadowshield/responders/blocker.py +0 -0
- {shadowshield-0.6.1 → shadowshield-0.6.3}/src/shadowshield/responders/isolator.py +0 -0
- {shadowshield-0.6.1 → shadowshield-0.6.3}/src/shadowshield/responders/rate_limiter.py +0 -0
- {shadowshield-0.6.1 → shadowshield-0.6.3}/src/shadowshield/responders/sanitizer.py +0 -0
- {shadowshield-0.6.1 → shadowshield-0.6.3}/src/shadowshield/server.py +0 -0
- {shadowshield-0.6.1 → shadowshield-0.6.3}/src/shadowshield/static/dashboard.html +0 -0
- {shadowshield-0.6.1 → shadowshield-0.6.3}/src/shadowshield/utils/__init__.py +0 -0
- {shadowshield-0.6.1 → shadowshield-0.6.3}/src/shadowshield/utils/logging.py +0 -0
- {shadowshield-0.6.1 → shadowshield-0.6.3}/src/shadowshield/utils/scoring.py +0 -0
- {shadowshield-0.6.1 → shadowshield-0.6.3}/src/shadowshield/utils/text.py +0 -0
- {shadowshield-0.6.1 → shadowshield-0.6.3}/tests/__init__.py +0 -0
- {shadowshield-0.6.1 → shadowshield-0.6.3}/tests/test_adversarial.py +0 -0
- {shadowshield-0.6.1 → shadowshield-0.6.3}/tests/test_agentic.py +0 -0
- {shadowshield-0.6.1 → shadowshield-0.6.3}/tests/test_mcp_guard.py +0 -0
- {shadowshield-0.6.1 → shadowshield-0.6.3}/tests/test_middleware.py +0 -0
- {shadowshield-0.6.1 → shadowshield-0.6.3}/tests/test_multilingual.py +0 -0
- {shadowshield-0.6.1 → shadowshield-0.6.3}/tests/test_pii_backends.py +0 -0
- {shadowshield-0.6.1 → shadowshield-0.6.3}/tests/test_policy.py +0 -0
- {shadowshield-0.6.1 → shadowshield-0.6.3}/tests/test_prompt_injection.py +0 -0
- {shadowshield-0.6.1 → shadowshield-0.6.3}/tests/test_responders.py +0 -0
- {shadowshield-0.6.1 → shadowshield-0.6.3}/tests/test_server.py +0 -0
- {shadowshield-0.6.1 → shadowshield-0.6.3}/tests/test_spans.py +0 -0
- {shadowshield-0.6.1 → shadowshield-0.6.3}/tests/test_telemetry.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: shadowshield
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.3
|
|
4
4
|
Summary: Unified open-source security shield for agentic AI systems — inspired by Sentinel & ShadowClaw.
|
|
5
5
|
Project-URL: Homepage, https://shadowshield.xyz
|
|
6
6
|
Project-URL: Documentation, https://github.com/0xsl1m/shadowshield#readme
|
|
@@ -28,7 +28,6 @@ Requires-Dist: httpx>=0.27
|
|
|
28
28
|
Requires-Dist: pydantic>=2.5
|
|
29
29
|
Requires-Dist: pyyaml>=6.0
|
|
30
30
|
Requires-Dist: structlog>=24.1
|
|
31
|
-
Requires-Dist: tiktoken>=0.6
|
|
32
31
|
Provides-Extra: all
|
|
33
32
|
Requires-Dist: datasets>=2.18; extra == 'all'
|
|
34
33
|
Requires-Dist: fastapi>=0.110; extra == 'all'
|
|
@@ -149,8 +148,9 @@ print(result.safe_text) # safe fallback message
|
|
|
149
148
|
> in-distribution **regression baseline, not a SOTA claim**. We publish the humbling
|
|
150
149
|
> external numbers on purpose — a credible security tool shows its homework.
|
|
151
150
|
> The frozen blind semantic snapshots are harder still: v1 reaches 26.7% recall /
|
|
152
|
-
> 13.3% FPR
|
|
153
|
-
> `shadowshield benchmark --generalization
|
|
151
|
+
> 13.3% FPR, v2 reaches 0% / 10%, and v3 reaches 30% / 30%; the 90-row aggregate
|
|
152
|
+
> is 22.2% / 20%. Run `shadowshield benchmark --generalization all`; these gaps
|
|
153
|
+
> are public by design.
|
|
154
154
|
|
|
155
155
|
---
|
|
156
156
|
|
|
@@ -206,8 +206,8 @@ pip install "shadowshield[dashboard]" # + FastAPI HTTP server & dashboard
|
|
|
206
206
|
pip install "shadowshield[all]" # everything
|
|
207
207
|
```
|
|
208
208
|
|
|
209
|
-
Core deps are intentionally small: `pydantic`, `structlog`, `pyyaml`,
|
|
210
|
-
`
|
|
209
|
+
Core deps are intentionally small: `pydantic`, `structlog`, `pyyaml`, and
|
|
210
|
+
`httpx`. The ML classifier, Presidio PII, dataset loaders, and dashboard live
|
|
211
211
|
behind extras — the default install pulls **no** heavy ML stack.
|
|
212
212
|
|
|
213
213
|
---
|
|
@@ -295,7 +295,7 @@ shadowshield detectors # list registered detectors
|
|
|
295
295
|
shadowshield init > shield.yaml # write an annotated default config
|
|
296
296
|
shadowshield benchmark # run the bundled offline benchmark
|
|
297
297
|
shadowshield benchmark --adversarial
|
|
298
|
-
shadowshield benchmark --generalization
|
|
298
|
+
shadowshield benchmark --generalization all # all frozen blind semantic snapshots
|
|
299
299
|
shadowshield serve # HTTP server + live dashboard (needs [dashboard])
|
|
300
300
|
```
|
|
301
301
|
|
|
@@ -329,15 +329,18 @@ export SHADOWSHIELD_ADMIN_KEY="$(openssl rand -hex 32)"
|
|
|
329
329
|
export SHADOWSHIELD_POLICY_KEY="$(openssl rand -hex 32)"
|
|
330
330
|
export SHADOWSHIELD_POLICY_STATE_KEY="$(openssl rand -hex 32)"
|
|
331
331
|
export SHADOWSHIELD_IMAGE_DIGEST="$(curl -fsSL \
|
|
332
|
-
https://github.com/0xsl1m/shadowshield/releases/download/v0.6.
|
|
332
|
+
https://github.com/0xsl1m/shadowshield/releases/download/v0.6.3/container-digest.txt)"
|
|
333
333
|
docker compose pull
|
|
334
334
|
docker compose up -d
|
|
335
335
|
```
|
|
336
336
|
|
|
337
|
-
The release workflow scans that exact image, publishes it to GHCR,
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
337
|
+
The release workflow scans that exact image, publishes it to GHCR, signs SLSA
|
|
338
|
+
provenance plus the CycloneDX SBOM with ephemeral GitHub OIDC/Sigstore identity,
|
|
339
|
+
verifies the registry-attached source/workflow claims and an anonymous digest
|
|
340
|
+
pull, and attaches the digest plus SBOM to the matching GitHub Release. Stable
|
|
341
|
+
release tags are accepted only from an exact green `main` commit. All four
|
|
342
|
+
secrets must be independent. Terminate TLS at a trusted ingress before exposing
|
|
343
|
+
it beyond localhost. See the
|
|
341
344
|
[production-readiness roadmap](docs/PRODUCTION_READINESS.md) for launch gates,
|
|
342
345
|
known scale limits, and the operator checklist.
|
|
343
346
|
|
|
@@ -347,14 +350,14 @@ Upgrading a control-plane volume from 0.6.0 requires an offline re-key because
|
|
|
347
350
|
```bash
|
|
348
351
|
# Load the existing scan/admin keys first so the new Compose file can resolve.
|
|
349
352
|
export SHADOWSHIELD_IMAGE_DIGEST="$(curl -fsSL \
|
|
350
|
-
https://github.com/0xsl1m/shadowshield/releases/download/v0.6.
|
|
353
|
+
https://github.com/0xsl1m/shadowshield/releases/download/v0.6.3/container-digest.txt)"
|
|
351
354
|
export SHADOWSHIELD_POLICY_KEY="<existing-0.6.0-policy-key>"
|
|
352
355
|
export SHADOWSHIELD_POLICY_STATE_KEY="$(openssl rand -hex 32)"
|
|
353
356
|
# Stop every writer and snapshot the volume before running the migration.
|
|
354
357
|
docker compose stop shadowshield
|
|
355
358
|
docker compose run --rm --no-deps shadowshield \
|
|
356
359
|
shadowshield migrate-policy-state --path /var/lib/shadowshield/policy-state.json
|
|
357
|
-
# Preserve the reported .pre-0.6.1.bak file, then start 0.6.
|
|
360
|
+
# Preserve the reported .pre-0.6.1.bak file, then start 0.6.3.
|
|
358
361
|
docker compose up -d
|
|
359
362
|
```
|
|
360
363
|
|
|
@@ -606,7 +609,7 @@ shadowshield serve --control --api-key SECRET # require X-API-Key / Bearer
|
|
|
606
609
|
shadowshield schema # config JSON Schema (editor/CI validation)
|
|
607
610
|
shadowshield owasp # OWASP LLM Top 10 (2025) coverage map
|
|
608
611
|
shadowshield benchmark --adversarial # curated regression set
|
|
609
|
-
shadowshield benchmark --generalization
|
|
612
|
+
shadowshield benchmark --generalization all # all frozen blind semantic snapshots
|
|
610
613
|
```
|
|
611
614
|
|
|
612
615
|
## Documentation
|
|
@@ -76,8 +76,9 @@ print(result.safe_text) # safe fallback message
|
|
|
76
76
|
> in-distribution **regression baseline, not a SOTA claim**. We publish the humbling
|
|
77
77
|
> external numbers on purpose — a credible security tool shows its homework.
|
|
78
78
|
> The frozen blind semantic snapshots are harder still: v1 reaches 26.7% recall /
|
|
79
|
-
> 13.3% FPR
|
|
80
|
-
> `shadowshield benchmark --generalization
|
|
79
|
+
> 13.3% FPR, v2 reaches 0% / 10%, and v3 reaches 30% / 30%; the 90-row aggregate
|
|
80
|
+
> is 22.2% / 20%. Run `shadowshield benchmark --generalization all`; these gaps
|
|
81
|
+
> are public by design.
|
|
81
82
|
|
|
82
83
|
---
|
|
83
84
|
|
|
@@ -133,8 +134,8 @@ pip install "shadowshield[dashboard]" # + FastAPI HTTP server & dashboard
|
|
|
133
134
|
pip install "shadowshield[all]" # everything
|
|
134
135
|
```
|
|
135
136
|
|
|
136
|
-
Core deps are intentionally small: `pydantic`, `structlog`, `pyyaml`,
|
|
137
|
-
`
|
|
137
|
+
Core deps are intentionally small: `pydantic`, `structlog`, `pyyaml`, and
|
|
138
|
+
`httpx`. The ML classifier, Presidio PII, dataset loaders, and dashboard live
|
|
138
139
|
behind extras — the default install pulls **no** heavy ML stack.
|
|
139
140
|
|
|
140
141
|
---
|
|
@@ -222,7 +223,7 @@ shadowshield detectors # list registered detectors
|
|
|
222
223
|
shadowshield init > shield.yaml # write an annotated default config
|
|
223
224
|
shadowshield benchmark # run the bundled offline benchmark
|
|
224
225
|
shadowshield benchmark --adversarial
|
|
225
|
-
shadowshield benchmark --generalization
|
|
226
|
+
shadowshield benchmark --generalization all # all frozen blind semantic snapshots
|
|
226
227
|
shadowshield serve # HTTP server + live dashboard (needs [dashboard])
|
|
227
228
|
```
|
|
228
229
|
|
|
@@ -256,15 +257,18 @@ export SHADOWSHIELD_ADMIN_KEY="$(openssl rand -hex 32)"
|
|
|
256
257
|
export SHADOWSHIELD_POLICY_KEY="$(openssl rand -hex 32)"
|
|
257
258
|
export SHADOWSHIELD_POLICY_STATE_KEY="$(openssl rand -hex 32)"
|
|
258
259
|
export SHADOWSHIELD_IMAGE_DIGEST="$(curl -fsSL \
|
|
259
|
-
https://github.com/0xsl1m/shadowshield/releases/download/v0.6.
|
|
260
|
+
https://github.com/0xsl1m/shadowshield/releases/download/v0.6.3/container-digest.txt)"
|
|
260
261
|
docker compose pull
|
|
261
262
|
docker compose up -d
|
|
262
263
|
```
|
|
263
264
|
|
|
264
|
-
The release workflow scans that exact image, publishes it to GHCR,
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
265
|
+
The release workflow scans that exact image, publishes it to GHCR, signs SLSA
|
|
266
|
+
provenance plus the CycloneDX SBOM with ephemeral GitHub OIDC/Sigstore identity,
|
|
267
|
+
verifies the registry-attached source/workflow claims and an anonymous digest
|
|
268
|
+
pull, and attaches the digest plus SBOM to the matching GitHub Release. Stable
|
|
269
|
+
release tags are accepted only from an exact green `main` commit. All four
|
|
270
|
+
secrets must be independent. Terminate TLS at a trusted ingress before exposing
|
|
271
|
+
it beyond localhost. See the
|
|
268
272
|
[production-readiness roadmap](docs/PRODUCTION_READINESS.md) for launch gates,
|
|
269
273
|
known scale limits, and the operator checklist.
|
|
270
274
|
|
|
@@ -274,14 +278,14 @@ Upgrading a control-plane volume from 0.6.0 requires an offline re-key because
|
|
|
274
278
|
```bash
|
|
275
279
|
# Load the existing scan/admin keys first so the new Compose file can resolve.
|
|
276
280
|
export SHADOWSHIELD_IMAGE_DIGEST="$(curl -fsSL \
|
|
277
|
-
https://github.com/0xsl1m/shadowshield/releases/download/v0.6.
|
|
281
|
+
https://github.com/0xsl1m/shadowshield/releases/download/v0.6.3/container-digest.txt)"
|
|
278
282
|
export SHADOWSHIELD_POLICY_KEY="<existing-0.6.0-policy-key>"
|
|
279
283
|
export SHADOWSHIELD_POLICY_STATE_KEY="$(openssl rand -hex 32)"
|
|
280
284
|
# Stop every writer and snapshot the volume before running the migration.
|
|
281
285
|
docker compose stop shadowshield
|
|
282
286
|
docker compose run --rm --no-deps shadowshield \
|
|
283
287
|
shadowshield migrate-policy-state --path /var/lib/shadowshield/policy-state.json
|
|
284
|
-
# Preserve the reported .pre-0.6.1.bak file, then start 0.6.
|
|
288
|
+
# Preserve the reported .pre-0.6.1.bak file, then start 0.6.3.
|
|
285
289
|
docker compose up -d
|
|
286
290
|
```
|
|
287
291
|
|
|
@@ -533,7 +537,7 @@ shadowshield serve --control --api-key SECRET # require X-API-Key / Bearer
|
|
|
533
537
|
shadowshield schema # config JSON Schema (editor/CI validation)
|
|
534
538
|
shadowshield owasp # OWASP LLM Top 10 (2025) coverage map
|
|
535
539
|
shadowshield benchmark --adversarial # curated regression set
|
|
536
|
-
shadowshield benchmark --generalization
|
|
540
|
+
shadowshield benchmark --generalization all # all frozen blind semantic snapshots
|
|
537
541
|
```
|
|
538
542
|
|
|
539
543
|
## Documentation
|
|
@@ -15,6 +15,8 @@ shadowshield benchmark --adversarial
|
|
|
15
15
|
# Independently authored blind semantic snapshots
|
|
16
16
|
shadowshield benchmark --generalization v1
|
|
17
17
|
shadowshield benchmark --generalization v2
|
|
18
|
+
shadowshield benchmark --generalization v3
|
|
19
|
+
shadowshield benchmark --generalization all
|
|
18
20
|
|
|
19
21
|
# External public dataset (needs the datasets extra)
|
|
20
22
|
pip install "shadowshield[datasets]"
|
|
@@ -24,14 +26,40 @@ pip install "shadowshield[transformers]"
|
|
|
24
26
|
shadowshield benchmark --hf deepset/prompt-injections --split test --transformer
|
|
25
27
|
```
|
|
26
28
|
|
|
29
|
+
The CLI warms every configured detector before starting the latency clock. Its
|
|
30
|
+
JSON and text reports include a runtime-integrity section covering warmup,
|
|
31
|
+
readiness, and bounded per-detector failure counts. A benchmark with a warmup
|
|
32
|
+
failure, a detector that remains unready, or any detector error exits non-zero
|
|
33
|
+
and is marked **unreliable**; its confusion counts remain available for
|
|
34
|
+
diagnosis but must not be published as quality evidence. CLI benchmark shields
|
|
35
|
+
disable normal audit emission so logging I/O does not distort latency or corrupt
|
|
36
|
+
the command's machine-readable JSON stdout.
|
|
37
|
+
|
|
38
|
+
Programmatic callers can request the same behavior with
|
|
39
|
+
`evaluate_shield(shield, examples, warmup=True)`. The default remains
|
|
40
|
+
`warmup=False` for API compatibility. In both modes, readiness is checked after
|
|
41
|
+
the scans and per-scan detector error counters are aggregated.
|
|
42
|
+
|
|
43
|
+
Per-category output retains the original `total`, `flagged`, and flag-rate
|
|
44
|
+
fields, and additionally reports class-conditional TP/FP/TN/FN, recall, FPR,
|
|
45
|
+
and balanced accuracy. Recall is `null`/`n/a` for categories with no attack
|
|
46
|
+
rows; FPR is `null`/`n/a` for categories with no benign rows; balanced accuracy
|
|
47
|
+
is defined only when both classes are present.
|
|
48
|
+
|
|
49
|
+
Aggregate and per-category recall/FPR also include two-sided 95% Wilson score
|
|
50
|
+
intervals. Wilson intervals remain informative at small sample sizes and at
|
|
51
|
+
observed rates of 0% or 100%; an interval is `null`/`n/a` when that category has
|
|
52
|
+
no rows for the corresponding class. Existing scalar metric fields are
|
|
53
|
+
unchanged, and the interval fields are additive.
|
|
54
|
+
|
|
27
55
|
## 1. Bundled benchmark (in-distribution — a regression baseline)
|
|
28
56
|
|
|
29
57
|
75 curated examples (40 attack / 35 benign, incl. 16 NotInject-style hard
|
|
30
58
|
negatives). `balanced` mode:
|
|
31
59
|
|
|
32
|
-
| recall | FPR | precision | F1 | p50 |
|
|
60
|
+
| recall (95% CI) | FPR (95% CI) | precision | F1 | p50 |
|
|
33
61
|
|---:|---:|---:|---:|---:|
|
|
34
|
-
| 100% | 0% | 100% | 100% | 0.16 ms |
|
|
62
|
+
| 100% [91.2%, 100%] | 0% [0%, 9.9%] | 100% | 100% | 0.16 ms |
|
|
35
63
|
|
|
36
64
|
**This is a regression baseline and a smoke test — NOT a claim of real-world
|
|
37
65
|
accuracy.** 100% on our own set just means we don't regress on the attack
|
|
@@ -43,34 +71,44 @@ The 36-row adversarial catalogue includes obfuscation, multilingual and indirect
|
|
|
43
71
|
attacks, plus benign trigger-heavy counterexamples. It improved from
|
|
44
72
|
15/2/16/3 to 18/0/18/0 (TP/FP/TN/FN):
|
|
45
73
|
|
|
46
|
-
| recall | FPR | precision | F1 |
|
|
74
|
+
| recall (95% CI) | FPR (95% CI) | precision | F1 |
|
|
47
75
|
|---:|---:|---:|---:|
|
|
48
|
-
| 100% | 0% | 100% | 100% |
|
|
76
|
+
| 100% [82.4%, 100%] | 0% [0%, 17.6%] | 100% | 100% |
|
|
49
77
|
|
|
50
78
|
This is still a curated regression set. The signatures were developed with these
|
|
51
79
|
cases visible, so its perfect score is not a generalization claim.
|
|
52
80
|
|
|
53
81
|
## 3. Blind semantic snapshots (the anti-gaming result)
|
|
54
82
|
|
|
55
|
-
|
|
83
|
+
Three balanced snapshots were authored independently without detector or regex
|
|
56
84
|
context, then frozen. They deliberately pair attacks with benign text containing
|
|
57
85
|
similar roleplay, authority, debug, and policy vocabulary.
|
|
58
86
|
|
|
59
|
-
Isolation protocol: the authoring
|
|
60
|
-
names, required row/balance counts, and a request for attack/benign
|
|
61
|
-
pairs.
|
|
62
|
-
|
|
87
|
+
Isolation protocol: the v1/v2 authoring tasks received only the five semantic
|
|
88
|
+
category names, required row/balance counts, and a request for attack/benign
|
|
89
|
+
contrast pairs. The v3 task received modality coverage requirements but no
|
|
90
|
+
detector source, signatures, or current predictions. The files were frozen before
|
|
91
|
+
detector tuning and are integrity-pinned in tests:
|
|
63
92
|
v1 SHA-256 `b3281ba1a42d266bb930bbb41943016d47b38dbc822ff7cff5131f3448a0248f`;
|
|
64
|
-
v2 SHA-256 `aa8b8c81c00a55bb65180e15ff743b6241d24845b3886e8e60b52b9b23db47fa
|
|
93
|
+
v2 SHA-256 `aa8b8c81c00a55bb65180e15ff743b6241d24845b3886e8e60b52b9b23db47fa`;
|
|
94
|
+
v3 SHA-256 `2285031e8143572311a522a4b6ec1a39c96a34ac2b42785f17818e8a145342bf`.
|
|
65
95
|
|
|
66
|
-
| snapshot | rows | TP/FP/TN/FN | recall | FPR | balanced accuracy |
|
|
96
|
+
| snapshot | rows | TP/FP/TN/FN | recall (95% CI) | FPR (95% CI) | balanced accuracy |
|
|
67
97
|
|---|---:|---:|---:|---:|---:|
|
|
68
|
-
| v1 | 30 | 4/2/13/11 | 26.7% | 13.3% | 56.7% |
|
|
69
|
-
| v2 | 20 | 0/1/9/10 | 0% | 10% | 45% |
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
98
|
+
| v1 | 30 | 4/2/13/11 | 26.7% [10.9%, 52.0%] | 13.3% [3.7%, 37.9%] | 56.7% |
|
|
99
|
+
| v2 | 20 | 0/1/9/10 | 0% [0%, 27.8%] | 10% [1.8%, 40.4%] | 45% |
|
|
100
|
+
| v3 | 40 | 6/6/14/14 | 30% [14.5%, 51.9%] | 30% [14.5%, 51.9%] | 50% |
|
|
101
|
+
| **aggregate** | **90** | **10/9/36/35** | **22.2% [12.5%, 36.3%]** | **20% [10.9%, 33.8%]** | **51.1%** |
|
|
102
|
+
|
|
103
|
+
The v3 snapshot was opened only after a detector candidate and acceptance bar
|
|
104
|
+
were frozen. That candidate raised aggregate recall to 55.6% but failed the
|
|
105
|
+
predeclared false-positive and balanced-accuracy gates (24.4% FPR; 65.6%
|
|
106
|
+
balanced accuracy), so it was discarded in full. No v3-driven detector tuning
|
|
107
|
+
ships in 0.6.2; the table records the unchanged released core.
|
|
108
|
+
|
|
109
|
+
These snapshots expose the core deterministic tier's semantic-generalization
|
|
110
|
+
ceiling and are development evidence for a fresh-corpus classifier/conjunction
|
|
111
|
+
tranche, not numbers to hide or a set to tune against.
|
|
74
112
|
|
|
75
113
|
## 4. External: `deepset/prompt-injections` (out-of-distribution)
|
|
76
114
|
|
|
@@ -1,21 +1,22 @@
|
|
|
1
1
|
# Production readiness roadmap
|
|
2
2
|
|
|
3
|
-
**Baseline:** ShadowShield 0.6.
|
|
3
|
+
**Baseline:** ShadowShield 0.6.3 · **Audit date:** 2026-07-25
|
|
4
4
|
|
|
5
5
|
This roadmap is the release gate for the Python library and the optional HTTP
|
|
6
|
-
control plane. A checked item is implemented and locally verified
|
|
7
|
-
items
|
|
6
|
+
control plane. A checked item is implemented and locally verified. The
|
|
7
|
+
operator-owned unchecked items do not block the library or static site, but they
|
|
8
|
+
must be resolved before exposing the optional HTTP control plane publicly.
|
|
8
9
|
|
|
9
10
|
## Current readiness
|
|
10
11
|
|
|
11
12
|
| Area | State | Evidence / remaining action |
|
|
12
13
|
|---|---|---|
|
|
13
|
-
| Correctness | Ready |
|
|
14
|
-
| Packaging | Ready |
|
|
15
|
-
| Runtime security | Ready with configuration | Early API-key/Bearer auth, body
|
|
16
|
-
| Supply chain | Ready | Digest-pinned
|
|
17
|
-
| Observability | Ready for single process | Content-free telemetry, Prometheus endpoint, bounded in-memory event feed |
|
|
18
|
-
| Detection quality | Beta | Curated adversarial: 100%/0% FPR; blind v1:
|
|
14
|
+
| Correctness | Ready | 320 unit/integration cases; strict mypy; Ruff lint/format; CodeQL extended scanning |
|
|
15
|
+
| Packaging | Ready | Hash-locked build environment, byte-for-byte repeat build gate, Twine validation, installed-wheel smoke test |
|
|
16
|
+
| Runtime security | Ready with configuration | Early API-key/Bearer auth, bounded/deadlined body intake, detector-failure policy, restricted CORS, immutable protection floor, non-root read-only container |
|
|
17
|
+
| Supply chain | Ready | Digest/SHA-pinned build inputs, PyPI OIDC, exact-green-main release gates, immutable image/evidence handling, pre-push vulnerability gate, anonymous GHCR digest pull, registry-verified SLSA and CycloneDX attestations |
|
|
18
|
+
| Observability | Ready for single process | Content-free telemetry, detector-error counters, Prometheus endpoint, bounded in-memory event feed |
|
|
19
|
+
| Detection quality | Beta | Curated adversarial: 100%/0% FPR; blind v1–v3 aggregate: 22.2%/20%; v3: 30%/30%; deepset core: 23.3%/0% |
|
|
19
20
|
| Scale / HA | Not yet | Counters, events, configuration, and rate limits are process-local |
|
|
20
21
|
| Streaming | Not yet | Outputs must be buffered before scanning |
|
|
21
22
|
|
|
@@ -23,7 +24,7 @@ items require an explicit operator or maintainer decision before public launch.
|
|
|
23
24
|
|
|
24
25
|
- [x] Repair control-plane syntax and restore test collection.
|
|
25
26
|
- [x] Make optional-dependency tests and typing deterministic with or without MCP installed.
|
|
26
|
-
- [x] Enforce an 80% branch-aware coverage floor (current local result:
|
|
27
|
+
- [x] Enforce an 80% branch-aware coverage floor (current local result: 86%).
|
|
27
28
|
- [x] Test every declared Python runtime, 3.10 through 3.14, in CI.
|
|
28
29
|
- [x] Build and validate both distribution formats and smoke-test the installed wheel.
|
|
29
30
|
- [x] Audit declared dependencies in CI (local audit: no known vulnerabilities).
|
|
@@ -48,6 +49,36 @@ items require an explicit operator or maintainer decision before public launch.
|
|
|
48
49
|
- [x] Pin the base image by digest, gate the exact release image on Trivy, publish
|
|
49
50
|
it to GHCR, verify an anonymous digest pull, and attach its immutable digest
|
|
50
51
|
plus CycloneDX SBOM to the release.
|
|
52
|
+
- [x] Sign release-image SLSA provenance and the CycloneDX SBOM with ephemeral
|
|
53
|
+
GitHub OIDC/Sigstore identity, then verify repository, workflow, runner, and
|
|
54
|
+
source-commit claims from the OCI registry before completing the release.
|
|
55
|
+
- [x] Reject release publication unless a stable, non-prerelease tag matches the
|
|
56
|
+
package version, resolves to `main`, and has a successful exact-SHA `main` CI run.
|
|
57
|
+
- [x] Reject symlink/non-regular durable policy-state files, use unpredictable
|
|
58
|
+
exclusive temporary files, and revision-check reads, migrations, and replaces.
|
|
59
|
+
- [x] Coalesce concurrent failed Transformer/Vector load generations while giving
|
|
60
|
+
every waiter a fresh bounded exception traceback and allowing a later retry.
|
|
61
|
+
- [x] Enable Dependabot security updates, extended CodeQL scanning, secret
|
|
62
|
+
push-protection, and repository-level enforcement of full-SHA Action pins.
|
|
63
|
+
- [x] Publish a third independently authored blind snapshot and reject—not
|
|
64
|
+
tune—the first frozen candidate when it missed the predeclared FPR/accuracy bar.
|
|
65
|
+
- [x] Make documented session guard calls record clean and blocked turns exactly
|
|
66
|
+
once so multi-turn and alignment checks receive the advertised trace.
|
|
67
|
+
- [x] Surface bounded, content-free detector failures in every result, audit,
|
|
68
|
+
benchmark, control event, and Prometheus metric; make strict mode fail closed.
|
|
69
|
+
- [x] Coalesce HTTP request frames, cap fragmentation, and apply one total body
|
|
70
|
+
deadline so authenticated slow/chunked bodies cannot pin all scan slots;
|
|
71
|
+
authenticate protected non-preflight methods before body/admission work.
|
|
72
|
+
- [x] Hash-lock release build and container dependencies, pin the Dockerfile
|
|
73
|
+
frontend, remove the unused mandatory tokenizer dependency, and require two
|
|
74
|
+
byte-identical distribution builds.
|
|
75
|
+
- [x] Upgrade maintained Actions to their Node 24 generations, bound every CI
|
|
76
|
+
job with a timeout, and refuse conflicting image tags or release assets while
|
|
77
|
+
allowing only attested exact-source digest recovery of an interrupted
|
|
78
|
+
container publication.
|
|
79
|
+
- [x] Make benchmark evidence fail visibly on warmup, readiness, or detector
|
|
80
|
+
errors and report class-conditional per-slice confusion metrics with 95%
|
|
81
|
+
Wilson confidence intervals.
|
|
51
82
|
|
|
52
83
|
## P1 — launch procedure (operator-owned)
|
|
53
84
|
|
|
@@ -60,7 +91,8 @@ items require an explicit operator or maintainer decision before public launch.
|
|
|
60
91
|
- [ ] Set an explicit CORS allowlist or leave CORS disabled.
|
|
61
92
|
- [ ] Run the adversarial and application-specific eval sets in permissive/shadow mode.
|
|
62
93
|
- [ ] Choose thresholds from measured false-positive cost; record the accepted baseline.
|
|
63
|
-
- [ ] Connect Prometheus and content-free telemetry; alert on
|
|
94
|
+
- [ ] Connect Prometheus and content-free telemetry; alert on detector errors,
|
|
95
|
+
readiness, block-rate, and latency shifts.
|
|
64
96
|
- [ ] Load-test representative payload sizes and concurrency on the target instance class.
|
|
65
97
|
- [ ] Record the deployed image digest and CI SBOM, then rehearse backup/restore
|
|
66
98
|
and rollback against the target environment.
|
|
@@ -70,11 +102,17 @@ items require an explicit operator or maintainer decision before public launch.
|
|
|
70
102
|
|
|
71
103
|
1. Add a rolling-window streaming scanner that blocks before unsafe output is emitted.
|
|
72
104
|
2. Move rate limits, event history, and metrics aggregation to shared external stores for multi-worker HA.
|
|
73
|
-
3.
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
105
|
+
3. Build a fresh independently sourced development corpus, then evaluate the
|
|
106
|
+
next detector candidate once on a new sealed snapshot and publish confidence intervals.
|
|
107
|
+
4. Add multi-hour soak and network fault-injection tests for reporters, judges, and hot policy updates.
|
|
108
|
+
5. Split the current administrator credential into narrower observe and mutation scopes.
|
|
109
|
+
6. Design cross-process model/index coordination and durable Reporter spooling.
|
|
110
|
+
7. Split unprivileged container build/scan from the write/OIDC publication job.
|
|
111
|
+
8. Refactor release evidence into a draft-first pipeline with independently
|
|
112
|
+
promoted, already-scanned OCI artifacts.
|
|
113
|
+
9. Pin optional model revisions, pre-cache them for offline production startup,
|
|
114
|
+
and bound model-loading time.
|
|
115
|
+
10. Add vulnerability-audit matrices for every supported optional dependency stack.
|
|
78
116
|
|
|
79
117
|
## Go / no-go gates
|
|
80
118
|
|
|
@@ -8,9 +8,14 @@ this specific GitHub workflow directly. The pipeline is
|
|
|
8
8
|
The same GitHub Release also triggers
|
|
9
9
|
[`container-release.yml`](../.github/workflows/container-release.yml). It builds
|
|
10
10
|
one image from the release tag, generates a CycloneDX SBOM, rejects fixable
|
|
11
|
-
high/critical findings, pushes that exact scanned image to GHCR,
|
|
12
|
-
|
|
13
|
-
the
|
|
11
|
+
high/critical findings, pushes that exact scanned image to GHCR, signs SLSA
|
|
12
|
+
provenance and CycloneDX attestations through GitHub's Sigstore-backed OIDC
|
|
13
|
+
service, verifies the signed provenance and an anonymous pull by digest, and
|
|
14
|
+
attaches `container-digest.txt` plus the SBOM to the GitHub Release.
|
|
15
|
+
|
|
16
|
+
Build tooling and the production container dependency graph are version- and
|
|
17
|
+
hash-locked in `requirements/build.lock` and `requirements/container.lock`.
|
|
18
|
+
Release jobs build the sdist and wheel twice and require byte-identical outputs.
|
|
14
19
|
|
|
15
20
|
## One-time setup (PyPI account owner only)
|
|
16
21
|
|
|
@@ -41,8 +46,15 @@ workflow deliberately fails its anonymous-pull gate if that happens.
|
|
|
41
46
|
1. Let the first `Publish release container` run push the package.
|
|
42
47
|
2. If the anonymous-pull step fails, open the `shadowshield` package settings
|
|
43
48
|
under the `0xsl1m` account and change visibility to **Public**.
|
|
44
|
-
3. Re-run the failed workflow job.
|
|
45
|
-
|
|
49
|
+
3. Re-run the failed workflow job. It will reuse an existing image only when the
|
|
50
|
+
version and exact-commit tags resolve to the same digest and its revision and
|
|
51
|
+
version labels match the release **and** that digest already has trusted source
|
|
52
|
+
provenance from this workflow for the exact commit. It never overwrites a
|
|
53
|
+
conflicting or unattested tag. If an interruption happens after an image push
|
|
54
|
+
but before provenance is attached, delete the incomplete version/commit tags
|
|
55
|
+
from GHCR and re-run; the workflow deliberately refuses to bless them.
|
|
56
|
+
Do not publish the digest to operators until the anonymous-pull and
|
|
57
|
+
evidence-attachment steps are green.
|
|
46
58
|
|
|
47
59
|
## Cutting a release
|
|
48
60
|
|
|
@@ -58,17 +70,18 @@ gh release create vX.Y.Z --target "$merge_sha" \
|
|
|
58
70
|
# -> PyPI publishing and exact-image scan/publish run independently
|
|
59
71
|
```
|
|
60
72
|
|
|
61
|
-
Do not create the release while required checks are pending.
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
73
|
+
Do not create the release while required checks are pending. Once trusted source
|
|
74
|
+
provenance exists, a partial container release is recoverable by fixing the
|
|
75
|
+
failed workflow and re-running it: exact matching registry content is pulled,
|
|
76
|
+
rescanned, re-attested, and reused. An interruption between the first image push
|
|
77
|
+
and provenance attachment intentionally needs the incomplete GHCR tags removed
|
|
78
|
+
before retry. Conflicting tags and existing evidence assets are never
|
|
79
|
+
overwritten. The PyPI version itself cannot be replaced.
|
|
67
80
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
81
|
+
Both publication workflows independently reject non-stable tags, tag/source
|
|
82
|
+
version mismatches, commits outside `main`, and commits without a successful
|
|
83
|
+
exact-SHA `main` CI run. There is no manual-dispatch publication path; use
|
|
84
|
+
GitHub's **Re-run failed jobs** control to recover a failed release workflow.
|
|
72
85
|
|
|
73
86
|
## Verify
|
|
74
87
|
|
|
@@ -79,13 +92,30 @@ python -c "import shadowshield as ss; print(ss.__version__)"
|
|
|
79
92
|
|
|
80
93
|
gh release download vX.Y.Z \
|
|
81
94
|
--pattern container-digest.txt --pattern shadowshield-sbom.cdx.json
|
|
82
|
-
|
|
95
|
+
digest="$(cat container-digest.txt)"
|
|
96
|
+
image="ghcr.io/0xsl1m/shadowshield@$digest"
|
|
97
|
+
docker pull "$image"
|
|
98
|
+
gh attestation verify "oci://$image" \
|
|
99
|
+
--repo 0xsl1m/shadowshield \
|
|
100
|
+
--bundle-from-oci \
|
|
101
|
+
--signer-workflow 0xsl1m/shadowshield/.github/workflows/container-release.yml \
|
|
102
|
+
--source-digest "$(git rev-list -n 1 vX.Y.Z)" \
|
|
103
|
+
--deny-self-hosted-runners
|
|
104
|
+
gh attestation verify "oci://$image" \
|
|
105
|
+
--repo 0xsl1m/shadowshield \
|
|
106
|
+
--bundle-from-oci \
|
|
107
|
+
--predicate-type https://cyclonedx.org/bom \
|
|
108
|
+
--signer-workflow 0xsl1m/shadowshield/.github/workflows/container-release.yml \
|
|
109
|
+
--source-digest "$(git rev-list -n 1 vX.Y.Z)" \
|
|
110
|
+
--deny-self-hosted-runners
|
|
83
111
|
```
|
|
84
112
|
|
|
85
113
|
## Notes
|
|
86
114
|
|
|
87
115
|
- **Versions are immutable.** A published version can never be re-uploaded, only
|
|
88
116
|
*yanked*. Always confirm `twine check` is green (CI does this) before releasing.
|
|
117
|
+
- Regenerate dependency locks only in a dedicated reviewed PR, using the exact
|
|
118
|
+
commands recorded at the top of each lock file; CI audits both locked graphs.
|
|
89
119
|
- Deploy the container by the exact `image@sha256` value in
|
|
90
120
|
`container-digest.txt`, never by a mutable version tag or a local Compose
|
|
91
121
|
rebuild. The release SBOM describes that exact pre-push-scanned image.
|
|
@@ -19,11 +19,11 @@ Generate an annotated starter file with `shadowshield init > shield.yaml`.
|
|
|
19
19
|
|
|
20
20
|
## Modes
|
|
21
21
|
|
|
22
|
-
| Mode | block_threshold | medium→ | LLM check | rate limit |
|
|
23
|
-
|
|
24
|
-
| `strict` | 0.45 | block | on | on (30/60s) |
|
|
25
|
-
| `balanced` | 0.65 | sanitize | off | off |
|
|
26
|
-
| `permissive` | 0.85 | flag | off | off |
|
|
22
|
+
| Mode | block_threshold | medium→ | detector failure | LLM check | rate limit |
|
|
23
|
+
|---|---|---|---|---|---|
|
|
24
|
+
| `strict` | 0.45 | block | block | on | on (30/60s) |
|
|
25
|
+
| `balanced` | 0.65 | sanitize | report | off | off |
|
|
26
|
+
| `permissive` | 0.85 | flag | report | off | off |
|
|
27
27
|
|
|
28
28
|
A mode seeds every default; any field you set in YAML or `for_mode(..., **kw)`
|
|
29
29
|
layers on top.
|
|
@@ -34,6 +34,7 @@ layers on top.
|
|
|
34
34
|
|---|---|
|
|
35
35
|
| `mode` | preset posture (strict/balanced/permissive) |
|
|
36
36
|
| `raise_on_block` | make `Shield.scan()` raise on a block (default false) |
|
|
37
|
+
| `fail_closed_on_detector_error` | block if any detector raises; failures are always reported as content-free metadata (strict defaults true) |
|
|
37
38
|
| `block_threshold` | aggregate score that forces a block regardless of policy |
|
|
38
39
|
| `policy.{none..critical}` | severity → decision mapping |
|
|
39
40
|
| `detectors.<name>.enabled` | per-detector toggle |
|
|
@@ -56,7 +56,9 @@ shield = Shield.for_mode(
|
|
|
56
56
|
|
|
57
57
|
A plugin that fails to import is **skipped**, not fatal — one broken third-party
|
|
58
58
|
plugin can never disable the shield. A plugin detector that *raises during a scan*
|
|
59
|
-
is caught by the engine's fail-safe wrapper
|
|
59
|
+
is caught by the engine's fail-safe wrapper, reported through bounded content-free
|
|
60
|
+
error metadata and metrics, and forces a block when
|
|
61
|
+
`fail_closed_on_detector_error` is enabled.
|
|
60
62
|
|
|
61
63
|
## Security note
|
|
62
64
|
|
|
@@ -67,8 +67,10 @@ identity to block on its own.
|
|
|
67
67
|
|
|
68
68
|
## Fail-safe defaults
|
|
69
69
|
|
|
70
|
-
- **Detector errors
|
|
71
|
-
|
|
70
|
+
- **Detector errors are visible.** The other independent layers continue, while
|
|
71
|
+
bounded content-free metadata and metrics identify the failed detector. Strict
|
|
72
|
+
mode forces a block; balanced/permissive report the failure unless configured
|
|
73
|
+
to fail closed.
|
|
72
74
|
- **`guard()` fails closed** (raises), **`filter()` fails soft** (returns the safe
|
|
73
75
|
fallback). You choose per call site.
|
|
74
76
|
- **Secrets are never echoed.** Secret matches are redacted from `Threat` records
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
[build-system]
|
|
2
|
-
requires = ["hatchling
|
|
2
|
+
requires = ["hatchling==1.31.0"]
|
|
3
3
|
build-backend = "hatchling.build"
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "shadowshield"
|
|
7
|
-
version = "0.6.
|
|
7
|
+
version = "0.6.3"
|
|
8
8
|
description = "Unified open-source security shield for agentic AI systems — inspired by Sentinel & ShadowClaw."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -43,7 +43,6 @@ dependencies = [
|
|
|
43
43
|
"structlog>=24.1",
|
|
44
44
|
"pyyaml>=6.0",
|
|
45
45
|
"httpx>=0.27",
|
|
46
|
-
"tiktoken>=0.6",
|
|
47
46
|
]
|
|
48
47
|
|
|
49
48
|
[project.optional-dependencies]
|
|
@@ -142,8 +141,17 @@ python_version = "3.10"
|
|
|
142
141
|
strict = true
|
|
143
142
|
warn_unused_ignores = true
|
|
144
143
|
ignore_missing_imports = true
|
|
144
|
+
# Allow targeted external-stub exclusions to respect ``follow_imports = skip``.
|
|
145
|
+
follow_imports_for_stubs = true
|
|
145
146
|
files = ["src/shadowshield"]
|
|
146
147
|
|
|
148
|
+
[[tool.mypy.overrides]]
|
|
149
|
+
# NumPy 2.5 publishes PEP 695 aliases that cannot be parsed while mypy checks
|
|
150
|
+
# our Python 3.10 syntax target. The optional dependency boundary stays Any;
|
|
151
|
+
# every ShadowShield source module is still checked explicitly and strictly.
|
|
152
|
+
module = ["numpy", "numpy.*"]
|
|
153
|
+
follow_imports = "skip"
|
|
154
|
+
|
|
147
155
|
[tool.coverage.run]
|
|
148
156
|
source = ["shadowshield"]
|
|
149
157
|
branch = true
|