stackone-defender 0.9.2__tar.gz → 0.9.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.
- stackone_defender-0.9.3/.release-please-manifest.json +1 -0
- {stackone_defender-0.9.2 → stackone_defender-0.9.3}/CHANGELOG.md +7 -0
- {stackone_defender-0.9.2 → stackone_defender-0.9.3}/PKG-INFO +16 -13
- {stackone_defender-0.9.2 → stackone_defender-0.9.3}/README.md +15 -12
- {stackone_defender-0.9.2 → stackone_defender-0.9.3}/pyproject.toml +1 -1
- stackone_defender-0.9.3/src/stackone_defender/models/minilm-multihead-v5/classifier_config.json +75 -0
- {stackone_defender-0.9.2 → stackone_defender-0.9.3}/src/stackone_defender/models/minilm-multihead-v5/model_quantized.onnx +0 -0
- stackone_defender-0.9.3/tests/conformance/VERSION +1 -0
- {stackone_defender-0.9.2 → stackone_defender-0.9.3}/tests/conformance/tier3-policy.json +31 -0
- {stackone_defender-0.9.2 → stackone_defender-0.9.3}/tests/conformance/tier3-redact.json +50 -0
- {stackone_defender-0.9.2 → stackone_defender-0.9.3}/tests/test_onnx_classifier.py +4 -2
- {stackone_defender-0.9.2 → stackone_defender-0.9.3}/tests/test_tier2_classifier.py +7 -6
- {stackone_defender-0.9.2 → stackone_defender-0.9.3}/uv.lock +7 -7
- stackone_defender-0.9.2/.release-please-manifest.json +0 -1
- stackone_defender-0.9.2/src/stackone_defender/models/minilm-multihead-v5/classifier_config.json +0 -47
- stackone_defender-0.9.2/tests/conformance/VERSION +0 -1
- {stackone_defender-0.9.2 → stackone_defender-0.9.3}/.gitattributes +0 -0
- {stackone_defender-0.9.2 → stackone_defender-0.9.3}/.gitignore +0 -0
- {stackone_defender-0.9.2 → stackone_defender-0.9.3}/.python-version +0 -0
- {stackone_defender-0.9.2 → stackone_defender-0.9.3}/.release-please-config.json +0 -0
- {stackone_defender-0.9.2 → stackone_defender-0.9.3}/LICENSE +0 -0
- {stackone_defender-0.9.2 → stackone_defender-0.9.3}/hatch_build.py +0 -0
- {stackone_defender-0.9.2 → stackone_defender-0.9.3}/prof.out +0 -0
- {stackone_defender-0.9.2 → stackone_defender-0.9.3}/scripts/sync_conformance.sh +0 -0
- {stackone_defender-0.9.2 → stackone_defender-0.9.3}/src/stackone_defender/__init__.py +0 -0
- {stackone_defender-0.9.2 → stackone_defender-0.9.3}/src/stackone_defender/classifiers/__init__.py +0 -0
- {stackone_defender-0.9.2 → stackone_defender-0.9.3}/src/stackone_defender/classifiers/onnx_classifier.py +0 -0
- {stackone_defender-0.9.2 → stackone_defender-0.9.3}/src/stackone_defender/classifiers/pattern_detector.py +0 -0
- {stackone_defender-0.9.2 → stackone_defender-0.9.3}/src/stackone_defender/classifiers/patterns.py +0 -0
- {stackone_defender-0.9.2 → stackone_defender-0.9.3}/src/stackone_defender/classifiers/tier2_classifier.py +0 -0
- {stackone_defender-0.9.2 → stackone_defender-0.9.3}/src/stackone_defender/classifiers/tier3_llm_provider.py +0 -0
- {stackone_defender-0.9.2 → stackone_defender-0.9.3}/src/stackone_defender/classifiers/tier3_openai_compatible.py +0 -0
- {stackone_defender-0.9.2 → stackone_defender-0.9.3}/src/stackone_defender/classifiers/tier3_orchestrator.py +0 -0
- {stackone_defender-0.9.2 → stackone_defender-0.9.3}/src/stackone_defender/config.py +0 -0
- {stackone_defender-0.9.2 → stackone_defender-0.9.3}/src/stackone_defender/core/__init__.py +0 -0
- {stackone_defender-0.9.2 → stackone_defender-0.9.3}/src/stackone_defender/core/prompt_defense.py +0 -0
- {stackone_defender-0.9.2 → stackone_defender-0.9.3}/src/stackone_defender/core/sentence_cleaner.py +0 -0
- {stackone_defender-0.9.2 → stackone_defender-0.9.3}/src/stackone_defender/core/tier3_input.py +0 -0
- {stackone_defender-0.9.2 → stackone_defender-0.9.3}/src/stackone_defender/core/tier3_policy.py +0 -0
- {stackone_defender-0.9.2 → stackone_defender-0.9.3}/src/stackone_defender/core/tool_result_sanitizer.py +0 -0
- {stackone_defender-0.9.2 → stackone_defender-0.9.3}/src/stackone_defender/models/minilm-multihead-v5/config.json +0 -0
- {stackone_defender-0.9.2 → stackone_defender-0.9.3}/src/stackone_defender/models/minilm-multihead-v5/tokenizer.json +0 -0
- {stackone_defender-0.9.2 → stackone_defender-0.9.3}/src/stackone_defender/models/minilm-multihead-v5/tokenizer_config.json +0 -0
- {stackone_defender-0.9.2 → stackone_defender-0.9.3}/src/stackone_defender/py.typed +0 -0
- {stackone_defender-0.9.2 → stackone_defender-0.9.3}/src/stackone_defender/sanitizers/__init__.py +0 -0
- {stackone_defender-0.9.2 → stackone_defender-0.9.3}/src/stackone_defender/sanitizers/encoding_detector.py +0 -0
- {stackone_defender-0.9.2 → stackone_defender-0.9.3}/src/stackone_defender/sanitizers/leet_normalizer.py +0 -0
- {stackone_defender-0.9.2 → stackone_defender-0.9.3}/src/stackone_defender/sanitizers/normalizer.py +0 -0
- {stackone_defender-0.9.2 → stackone_defender-0.9.3}/src/stackone_defender/sanitizers/role_stripper.py +0 -0
- {stackone_defender-0.9.2 → stackone_defender-0.9.3}/src/stackone_defender/sfe/__init__.py +0 -0
- {stackone_defender-0.9.2 → stackone_defender-0.9.3}/src/stackone_defender/sfe/model.ftz +0 -0
- {stackone_defender-0.9.2 → stackone_defender-0.9.3}/src/stackone_defender/sfe/preprocess.py +0 -0
- {stackone_defender-0.9.2 → stackone_defender-0.9.3}/src/stackone_defender/types.py +0 -0
- {stackone_defender-0.9.2 → stackone_defender-0.9.3}/src/stackone_defender/utils/__init__.py +0 -0
- {stackone_defender-0.9.2 → stackone_defender-0.9.3}/src/stackone_defender/utils/boundary.py +0 -0
- {stackone_defender-0.9.2 → stackone_defender-0.9.3}/src/stackone_defender/utils/field_detection.py +0 -0
- {stackone_defender-0.9.2 → stackone_defender-0.9.3}/src/stackone_defender/utils/lfs.py +0 -0
- {stackone_defender-0.9.2 → stackone_defender-0.9.3}/src/stackone_defender/utils/redact.py +0 -0
- {stackone_defender-0.9.2 → stackone_defender-0.9.3}/src/stackone_defender/utils/structure.py +0 -0
- {stackone_defender-0.9.2 → stackone_defender-0.9.3}/src/stackone_defender/utils/text.py +0 -0
- {stackone_defender-0.9.2 → stackone_defender-0.9.3}/tests/__init__.py +0 -0
- {stackone_defender-0.9.2 → stackone_defender-0.9.3}/tests/conformance/README.md +0 -0
- {stackone_defender-0.9.2 → stackone_defender-0.9.3}/tests/conformance/tier3-parse.json +0 -0
- {stackone_defender-0.9.2 → stackone_defender-0.9.3}/tests/conformance/tier3-prompt.json +0 -0
- {stackone_defender-0.9.2 → stackone_defender-0.9.3}/tests/conformance/tier3-serialize.json +0 -0
- {stackone_defender-0.9.2 → stackone_defender-0.9.3}/tests/conformance_known_failures.txt +0 -0
- {stackone_defender-0.9.2 → stackone_defender-0.9.3}/tests/test_conformance.py +0 -0
- {stackone_defender-0.9.2 → stackone_defender-0.9.3}/tests/test_integration.py +0 -0
- {stackone_defender-0.9.2 → stackone_defender-0.9.3}/tests/test_lfs.py +0 -0
- {stackone_defender-0.9.2 → stackone_defender-0.9.3}/tests/test_pattern_detector.py +0 -0
- {stackone_defender-0.9.2 → stackone_defender-0.9.3}/tests/test_sanitizers.py +0 -0
- {stackone_defender-0.9.2 → stackone_defender-0.9.3}/tests/test_sentence_cleaner.py +0 -0
- {stackone_defender-0.9.2 → stackone_defender-0.9.3}/tests/test_sfe.py +0 -0
- {stackone_defender-0.9.2 → stackone_defender-0.9.3}/tests/test_tier3.py +0 -0
- {stackone_defender-0.9.2 → stackone_defender-0.9.3}/tests/test_tier3_policy.py +0 -0
- {stackone_defender-0.9.2 → stackone_defender-0.9.3}/tests/test_utils.py +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{".":"0.9.3"}
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.9.3](https://github.com/StackOneHQ/defender-py/compare/stackone-defender-v0.9.2...stackone-defender-v0.9.3) (2026-10-20)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* **ENG-3282:** swap the Tier-2 model and recalibrate its threshold ([#44](https://github.com/StackOneHQ/defender-py/issues/44)) ([5fd8f87](https://github.com/StackOneHQ/defender-py/commit/5fd8f8748bcc72a42324b5fba09d90465b062135))
|
|
9
|
+
|
|
3
10
|
## [0.9.2](https://github.com/StackOneHQ/defender-py/compare/stackone-defender-v0.9.1...stackone-defender-v0.9.2) (2026-10-20)
|
|
4
11
|
|
|
5
12
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: stackone-defender
|
|
3
|
-
Version: 0.9.
|
|
3
|
+
Version: 0.9.3
|
|
4
4
|
Summary: Indirect prompt injection defense for AI agents using tool calls
|
|
5
5
|
Project-URL: Homepage, https://defender.stackone.com/?utm_source=pypi&utm_medium=listing&utm_campaign=homepage
|
|
6
6
|
Project-URL: Repository, https://github.com/StackOneHQ/defender-py
|
|
@@ -49,7 +49,7 @@ Description-Content-Type: text/markdown
|
|
|
49
49
|
<img src="https://img.shields.io/badge/model-22MB-047B43?style=flat-square" alt="Model size: 22MB" />
|
|
50
50
|
<img src="https://img.shields.io/badge/latency-~10ms-047B43?style=flat-square" alt="Latency: ~10ms" />
|
|
51
51
|
<img src="https://img.shields.io/badge/CPU--only-no%20GPU%20needed-047B43?style=flat-square" alt="CPU only" />
|
|
52
|
-
<img src="https://img.shields.io/badge/F1%20Score-
|
|
52
|
+
<img src="https://img.shields.io/badge/F1%20Score-86.5%25-047B43?style=flat-square" alt="F1 Score: 86.5%" />
|
|
53
53
|
</p>
|
|
54
54
|
|
|
55
55
|
</div>
|
|
@@ -142,8 +142,9 @@ Packed-chunk MiniLM classifier (int8 ONNX ~22 MB, bundled):
|
|
|
142
142
|
- Split text into sentences, pack to model-sized chunks, score chunks in batched ONNX calls
|
|
143
143
|
- Catches paraphrased or novel injections missed by regex
|
|
144
144
|
- Uses chunked batch inference to bound memory on large payloads
|
|
145
|
-
- The bundled model is multi-head
|
|
146
|
-
- The bundled model carries calibrated defaults in its `classifier_config.json` (`high_risk_threshold` 0.
|
|
145
|
+
- The bundled model is multi-head (variant `jbv2-mh-grid-agpjb2pinstr-s2` since 0.9.3; it still lives in the `minilm-multihead-v5` folder so existing paths keep working). Its auxiliary head flags meta-discussion and documentation that quotes injection text.
|
|
146
|
+
- The bundled model carries calibrated defaults in its `classifier_config.json` (`high_risk_threshold` 0.52 at temperature 2.41). They override library defaults when the model loads. Thresholds are model-specific: 0.64 was right for the previous model, not this one.
|
|
147
|
+
- Multi-head mode is off by default; Tier 2 then rates a chunk high at `high_risk_threshold` (blocking also needs `block_high_risk=True`). To turn it on, pass a preset as `tier2_config={"multihead": MultiheadConfig(main_threshold=..., aux_threshold=...)}`: a chunk then blocks when its main score >= `main_threshold` and its aux score < `aux_threshold`, and the result reports `tier2_aux_score` and `tier2_multihead_blocked`. Presets tuned for this model: High 0.77/0.325, Medium 0.88/0.325, Low 0.94/0.30. They're also in `classifier_config.json` under `multihead_presets`, but defender doesn't load them for you.
|
|
147
148
|
|
|
148
149
|
### Optional SFE preprocessor
|
|
149
150
|
|
|
@@ -152,14 +153,16 @@ Packed-chunk MiniLM classifier (int8 ONNX ~22 MB, bundled):
|
|
|
152
153
|
- **Tier 2** extracts strings from the SFE-filtered tree; `fields_dropped` lists paths omitted from that extraction (not removed from `sanitized`)
|
|
153
154
|
- Fails open if the runtime/model is unavailable: payload continues unfiltered
|
|
154
155
|
|
|
155
|
-
**Benchmarks** (
|
|
156
|
+
**Benchmarks** (Tier 2 only, through the package pipeline at the default threshold 0.52; measured 2026-10-20):
|
|
156
157
|
|
|
157
|
-
| Benchmark | F1 | Samples |
|
|
158
|
-
|
|
159
|
-
| Qualifire (in
|
|
160
|
-
| xxz224 (out-of-distribution) | 0.
|
|
161
|
-
| jayavibhav (
|
|
162
|
-
| **Average** | **0.
|
|
158
|
+
| Benchmark | F1 | Precision | Recall | Samples |
|
|
159
|
+
|-----------|-----|-----------|--------|---------|
|
|
160
|
+
| Qualifire (held-out test split; the dataset's train split is used in training) | 0.828 | 0.857 | 0.800 | 751 |
|
|
161
|
+
| xxz224 (out-of-distribution) | 0.814 | 0.989 | 0.691 | ~22.5k |
|
|
162
|
+
| jayavibhav (unseen rows; other rows of this split are used in training) | 0.955 | 0.960 | 0.950 | ~60k |
|
|
163
|
+
| **Average** | **0.865** | | | |
|
|
164
|
+
|
|
165
|
+
The previous model scores 0.843 on the same sets and method. Earlier READMEs showed 0.908 from a different method (raw scores, best threshold picked per dataset), which isn't comparable. Latency is about 10 ms per call (p50 on a 2-vCPU container, short inputs); long inputs that split into several chunks take longer.
|
|
163
166
|
|
|
164
167
|
### Optional Tier 3 — LLM adjudication (bring your own LLM)
|
|
165
168
|
|
|
@@ -269,7 +272,7 @@ A failed chunk is skipped (fail open), and `result.tier3.errored` and `coverage_
|
|
|
269
272
|
| Option | Default | What it does |
|
|
270
273
|
|---|---|---|
|
|
271
274
|
| `allow_override` | `True` | Cascade: whether a Tier-3 `allow` can undo a Tier-2 block. `False`: Tier 3 only adds blocks. `{"max_tier2_score": x}`: only when Tier 2's undamped score is below `x`. A general-purpose LLM can be talked into `allow`, so prefer `False` or a cap. Defender warns once when it's `True` with a provider from `create_llm_tier3_provider` or `create_openai_compatible_tier3_provider`. |
|
|
272
|
-
| `on_error` | cascade `"tier2"`, tier3_only `"allow"` | What a failure (error, timeout, invalid verdict) does. `"tier2"` keeps Tier 2's verdict. `"allow"` treats it as an allow. `"block"` blocks: in cascade when Tier 2 rated the payload medium or higher; in tier3_only when any chunk failed (and `on_oversize` then defaults to `"scan_anyway"`). A missing provider fails closed too. `"tier2"` isn't valid in tier3_only: defender warns and uses `"allow"`. |
|
|
275
|
+
| `on_error` | cascade `"tier2"`, tier3_only `"allow"` | What a failure (error, timeout, invalid verdict) does. `"tier2"` keeps Tier 2's verdict. `"allow"` treats it as an allow. `"block"` blocks: in cascade when Tier 2 rated the payload medium or higher (with the bundled model Tier 2 rarely reports `medium`: its band is [0.5, 0.52)); in tier3_only when any chunk failed (and `on_oversize` then defaults to `"scan_anyway"`). A missing provider fails closed too. `"tier2"` isn't valid in tier3_only: defender warns and uses `"allow"`. |
|
|
273
276
|
| `decide` | none | `decide(Tier3DecisionInput) -> "block" \| "allow" \| "default"`, called once per call after Tier 3 ran or failed. Replaces `allow_override` and `on_error` for that call. Gets metadata only (never content). Must be synchronous. A raise or bad return means `"default"`. It can't undo a Tier 1 detection, override `on_oversize`, or block when `block_high_risk=False`. |
|
|
274
277
|
| `timeout_ms` | none | Per provider call, from when it gets a concurrency slot. An async provider is cancelled at the deadline (`ctx["deadline"]`); a sync one can't be interrupted. |
|
|
275
278
|
| `max_concurrency` | unbounded | Max in-flight provider calls per instance, per event loop. Each sync `defend_tool_result` call runs its own loop, so the cap doesn't span threads that use the sync API. Set `timeout_ms` with it. |
|
|
@@ -314,7 +317,7 @@ defense = create_prompt_defense(
|
|
|
314
317
|
annotate_boundary=False, # True: wrap risky strings with [UD-…] tags (npm: annotateBoundary)
|
|
315
318
|
sanitize_content=True, # False: detect-and-gate; sanitized is the input verbatim
|
|
316
319
|
tier2_fields=["subject", "body", "snippet"], # optional: scope Tier 2 to these JSON keys (default: all strings)
|
|
317
|
-
tier2_config={"high_risk_threshold": 0.
|
|
320
|
+
tier2_config={"high_risk_threshold": 0.52}, # optional; defaults come from the bundled model's classifier_config.json
|
|
318
321
|
use_sfe=False, # True, or {"threshold": 0.5, "predictor": ...}: SFE preprocessing before Tier 2
|
|
319
322
|
enable_tier3=False,
|
|
320
323
|
defender_mode="cascade", # or "tier3_only"; ignored unless enable_tier3
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
<img src="https://img.shields.io/badge/model-22MB-047B43?style=flat-square" alt="Model size: 22MB" />
|
|
20
20
|
<img src="https://img.shields.io/badge/latency-~10ms-047B43?style=flat-square" alt="Latency: ~10ms" />
|
|
21
21
|
<img src="https://img.shields.io/badge/CPU--only-no%20GPU%20needed-047B43?style=flat-square" alt="CPU only" />
|
|
22
|
-
<img src="https://img.shields.io/badge/F1%20Score-
|
|
22
|
+
<img src="https://img.shields.io/badge/F1%20Score-86.5%25-047B43?style=flat-square" alt="F1 Score: 86.5%" />
|
|
23
23
|
</p>
|
|
24
24
|
|
|
25
25
|
</div>
|
|
@@ -112,8 +112,9 @@ Packed-chunk MiniLM classifier (int8 ONNX ~22 MB, bundled):
|
|
|
112
112
|
- Split text into sentences, pack to model-sized chunks, score chunks in batched ONNX calls
|
|
113
113
|
- Catches paraphrased or novel injections missed by regex
|
|
114
114
|
- Uses chunked batch inference to bound memory on large payloads
|
|
115
|
-
- The bundled model is multi-head
|
|
116
|
-
- The bundled model carries calibrated defaults in its `classifier_config.json` (`high_risk_threshold` 0.
|
|
115
|
+
- The bundled model is multi-head (variant `jbv2-mh-grid-agpjb2pinstr-s2` since 0.9.3; it still lives in the `minilm-multihead-v5` folder so existing paths keep working). Its auxiliary head flags meta-discussion and documentation that quotes injection text.
|
|
116
|
+
- The bundled model carries calibrated defaults in its `classifier_config.json` (`high_risk_threshold` 0.52 at temperature 2.41). They override library defaults when the model loads. Thresholds are model-specific: 0.64 was right for the previous model, not this one.
|
|
117
|
+
- Multi-head mode is off by default; Tier 2 then rates a chunk high at `high_risk_threshold` (blocking also needs `block_high_risk=True`). To turn it on, pass a preset as `tier2_config={"multihead": MultiheadConfig(main_threshold=..., aux_threshold=...)}`: a chunk then blocks when its main score >= `main_threshold` and its aux score < `aux_threshold`, and the result reports `tier2_aux_score` and `tier2_multihead_blocked`. Presets tuned for this model: High 0.77/0.325, Medium 0.88/0.325, Low 0.94/0.30. They're also in `classifier_config.json` under `multihead_presets`, but defender doesn't load them for you.
|
|
117
118
|
|
|
118
119
|
### Optional SFE preprocessor
|
|
119
120
|
|
|
@@ -122,14 +123,16 @@ Packed-chunk MiniLM classifier (int8 ONNX ~22 MB, bundled):
|
|
|
122
123
|
- **Tier 2** extracts strings from the SFE-filtered tree; `fields_dropped` lists paths omitted from that extraction (not removed from `sanitized`)
|
|
123
124
|
- Fails open if the runtime/model is unavailable: payload continues unfiltered
|
|
124
125
|
|
|
125
|
-
**Benchmarks** (
|
|
126
|
+
**Benchmarks** (Tier 2 only, through the package pipeline at the default threshold 0.52; measured 2026-10-20):
|
|
126
127
|
|
|
127
|
-
| Benchmark | F1 | Samples |
|
|
128
|
-
|
|
129
|
-
| Qualifire (in
|
|
130
|
-
| xxz224 (out-of-distribution) | 0.
|
|
131
|
-
| jayavibhav (
|
|
132
|
-
| **Average** | **0.
|
|
128
|
+
| Benchmark | F1 | Precision | Recall | Samples |
|
|
129
|
+
|-----------|-----|-----------|--------|---------|
|
|
130
|
+
| Qualifire (held-out test split; the dataset's train split is used in training) | 0.828 | 0.857 | 0.800 | 751 |
|
|
131
|
+
| xxz224 (out-of-distribution) | 0.814 | 0.989 | 0.691 | ~22.5k |
|
|
132
|
+
| jayavibhav (unseen rows; other rows of this split are used in training) | 0.955 | 0.960 | 0.950 | ~60k |
|
|
133
|
+
| **Average** | **0.865** | | | |
|
|
134
|
+
|
|
135
|
+
The previous model scores 0.843 on the same sets and method. Earlier READMEs showed 0.908 from a different method (raw scores, best threshold picked per dataset), which isn't comparable. Latency is about 10 ms per call (p50 on a 2-vCPU container, short inputs); long inputs that split into several chunks take longer.
|
|
133
136
|
|
|
134
137
|
### Optional Tier 3 — LLM adjudication (bring your own LLM)
|
|
135
138
|
|
|
@@ -239,7 +242,7 @@ A failed chunk is skipped (fail open), and `result.tier3.errored` and `coverage_
|
|
|
239
242
|
| Option | Default | What it does |
|
|
240
243
|
|---|---|---|
|
|
241
244
|
| `allow_override` | `True` | Cascade: whether a Tier-3 `allow` can undo a Tier-2 block. `False`: Tier 3 only adds blocks. `{"max_tier2_score": x}`: only when Tier 2's undamped score is below `x`. A general-purpose LLM can be talked into `allow`, so prefer `False` or a cap. Defender warns once when it's `True` with a provider from `create_llm_tier3_provider` or `create_openai_compatible_tier3_provider`. |
|
|
242
|
-
| `on_error` | cascade `"tier2"`, tier3_only `"allow"` | What a failure (error, timeout, invalid verdict) does. `"tier2"` keeps Tier 2's verdict. `"allow"` treats it as an allow. `"block"` blocks: in cascade when Tier 2 rated the payload medium or higher; in tier3_only when any chunk failed (and `on_oversize` then defaults to `"scan_anyway"`). A missing provider fails closed too. `"tier2"` isn't valid in tier3_only: defender warns and uses `"allow"`. |
|
|
245
|
+
| `on_error` | cascade `"tier2"`, tier3_only `"allow"` | What a failure (error, timeout, invalid verdict) does. `"tier2"` keeps Tier 2's verdict. `"allow"` treats it as an allow. `"block"` blocks: in cascade when Tier 2 rated the payload medium or higher (with the bundled model Tier 2 rarely reports `medium`: its band is [0.5, 0.52)); in tier3_only when any chunk failed (and `on_oversize` then defaults to `"scan_anyway"`). A missing provider fails closed too. `"tier2"` isn't valid in tier3_only: defender warns and uses `"allow"`. |
|
|
243
246
|
| `decide` | none | `decide(Tier3DecisionInput) -> "block" \| "allow" \| "default"`, called once per call after Tier 3 ran or failed. Replaces `allow_override` and `on_error` for that call. Gets metadata only (never content). Must be synchronous. A raise or bad return means `"default"`. It can't undo a Tier 1 detection, override `on_oversize`, or block when `block_high_risk=False`. |
|
|
244
247
|
| `timeout_ms` | none | Per provider call, from when it gets a concurrency slot. An async provider is cancelled at the deadline (`ctx["deadline"]`); a sync one can't be interrupted. |
|
|
245
248
|
| `max_concurrency` | unbounded | Max in-flight provider calls per instance, per event loop. Each sync `defend_tool_result` call runs its own loop, so the cap doesn't span threads that use the sync API. Set `timeout_ms` with it. |
|
|
@@ -284,7 +287,7 @@ defense = create_prompt_defense(
|
|
|
284
287
|
annotate_boundary=False, # True: wrap risky strings with [UD-…] tags (npm: annotateBoundary)
|
|
285
288
|
sanitize_content=True, # False: detect-and-gate; sanitized is the input verbatim
|
|
286
289
|
tier2_fields=["subject", "body", "snippet"], # optional: scope Tier 2 to these JSON keys (default: all strings)
|
|
287
|
-
tier2_config={"high_risk_threshold": 0.
|
|
290
|
+
tier2_config={"high_risk_threshold": 0.52}, # optional; defaults come from the bundled model's classifier_config.json
|
|
288
291
|
use_sfe=False, # True, or {"threshold": 0.5, "predictor": ...}: SFE preprocessing before Tier 2
|
|
289
292
|
enable_tier3=False,
|
|
290
293
|
defender_mode="cascade", # or "tier3_only"; ignored unless enable_tier3
|
stackone_defender-0.9.3/src/stackone_defender/models/minilm-multihead-v5/classifier_config.json
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
{
|
|
2
|
+
"model_id": "sentence-transformers/all-MiniLM-L6-v2",
|
|
3
|
+
"model_type": "minilm",
|
|
4
|
+
"use_seq_clf": false,
|
|
5
|
+
"hidden_size": 384,
|
|
6
|
+
"freeze_layers": 4,
|
|
7
|
+
"pooling": "mean",
|
|
8
|
+
"optimal_threshold": 0.15,
|
|
9
|
+
"datasets": [
|
|
10
|
+
"qualifire",
|
|
11
|
+
"jayavibhav",
|
|
12
|
+
"agentdojo",
|
|
13
|
+
"jasperls",
|
|
14
|
+
"jailbreakbench",
|
|
15
|
+
"toxic-chat",
|
|
16
|
+
"chatgpt-jailbreaks",
|
|
17
|
+
"email-hardneg",
|
|
18
|
+
"email-hardneg-gen",
|
|
19
|
+
"multilingual-hardneg",
|
|
20
|
+
"jailbreakbench-neg",
|
|
21
|
+
"toxic-chat-neg",
|
|
22
|
+
"fujitsu-injecagent",
|
|
23
|
+
"fujitsu-rag",
|
|
24
|
+
"enron-ham",
|
|
25
|
+
"connector-hardneg-v2",
|
|
26
|
+
"dev-tooling-hardneg-curated",
|
|
27
|
+
"dev-tooling-attacks",
|
|
28
|
+
"agentshield-shape-attacks",
|
|
29
|
+
"system-prompt-extraction-attacks",
|
|
30
|
+
"emoji-ci-benign",
|
|
31
|
+
"benign-user-queries",
|
|
32
|
+
"code-docs-benign"
|
|
33
|
+
],
|
|
34
|
+
"token_level": false,
|
|
35
|
+
"token_pool": "max",
|
|
36
|
+
"token_topk": 5,
|
|
37
|
+
"three_class": false,
|
|
38
|
+
"multi_head": true,
|
|
39
|
+
"aux_loss_alpha": 0.5,
|
|
40
|
+
"extra_benchmarks": [
|
|
41
|
+
"hd-negatives-v1",
|
|
42
|
+
"hd-pairs-v1",
|
|
43
|
+
"hd-negatives-v2",
|
|
44
|
+
"hd-pairs-v2",
|
|
45
|
+
"hd-negatives-v4",
|
|
46
|
+
"hd-pairs-v4",
|
|
47
|
+
"hd-short-v1",
|
|
48
|
+
"hd-negatives-v5",
|
|
49
|
+
"hd-pairs-v5",
|
|
50
|
+
"hd-pairs-agent-v1"
|
|
51
|
+
],
|
|
52
|
+
"variant": "jbv2-mh-grid-agpjb2pinstr-s2",
|
|
53
|
+
"calibration": {
|
|
54
|
+
"temperatureT": 2.41,
|
|
55
|
+
"highRiskThreshold": 0.52,
|
|
56
|
+
"ece": 0.09,
|
|
57
|
+
"fitted_on": "labeled plugin events 2026-05-13",
|
|
58
|
+
"notes": "tier2Score is the calibrated probability sigmoid(logit/T) at T=2.41. highRiskThreshold 0.52 is the single-head operating point for this model, chosen to match the shipped false-positive rate (v5 used 0.64). Do not reuse 0.64 with this model."
|
|
59
|
+
},
|
|
60
|
+
"multihead_presets": {
|
|
61
|
+
"High": {
|
|
62
|
+
"mainThreshold": 0.77,
|
|
63
|
+
"auxThreshold": 0.325
|
|
64
|
+
},
|
|
65
|
+
"Medium": {
|
|
66
|
+
"mainThreshold": 0.88,
|
|
67
|
+
"auxThreshold": 0.325
|
|
68
|
+
},
|
|
69
|
+
"Low": {
|
|
70
|
+
"mainThreshold": 0.94,
|
|
71
|
+
"auxThreshold": 0.3
|
|
72
|
+
},
|
|
73
|
+
"notes": "Tuned 2026-10-14 for this model only (docs/2026-09-30-tier2-prompt-injection-identifiability.md, release gate 1). Do not reuse for another model."
|
|
74
|
+
}
|
|
75
|
+
}
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
defender-v0.9.3
|
|
@@ -1737,6 +1737,37 @@
|
|
|
1737
1737
|
"coverageDegraded": false,
|
|
1738
1738
|
"judgeCalls": 1
|
|
1739
1739
|
}
|
|
1740
|
+
},
|
|
1741
|
+
{
|
|
1742
|
+
"id": "t3only-refused-without-decision-blocks",
|
|
1743
|
+
"description": "A refusal blocks even without a valid decision.",
|
|
1744
|
+
"input": {
|
|
1745
|
+
"mode": "tier3_only",
|
|
1746
|
+
"payload": {
|
|
1747
|
+
"body": "x"
|
|
1748
|
+
},
|
|
1749
|
+
"judge": {
|
|
1750
|
+
"default": {
|
|
1751
|
+
"refused": true
|
|
1752
|
+
}
|
|
1753
|
+
}
|
|
1754
|
+
},
|
|
1755
|
+
"expect": {
|
|
1756
|
+
"allowed": false,
|
|
1757
|
+
"riskLevel": "high",
|
|
1758
|
+
"tier3": {
|
|
1759
|
+
"decision": "block",
|
|
1760
|
+
"refused": true,
|
|
1761
|
+
"hasRaw": false
|
|
1762
|
+
},
|
|
1763
|
+
"coverageDegraded": false,
|
|
1764
|
+
"chunkSummary": {
|
|
1765
|
+
"chunks": 1,
|
|
1766
|
+
"blocked": 1,
|
|
1767
|
+
"blockingChunk": 0
|
|
1768
|
+
},
|
|
1769
|
+
"judgeCalls": 1
|
|
1770
|
+
}
|
|
1740
1771
|
}
|
|
1741
1772
|
]
|
|
1742
1773
|
}
|
|
@@ -237,6 +237,56 @@
|
|
|
237
237
|
"expect": {
|
|
238
238
|
"skipReason": "Tier 3 provider error: 😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀…"
|
|
239
239
|
}
|
|
240
|
+
},
|
|
241
|
+
{
|
|
242
|
+
"id": "url-password-with-at",
|
|
243
|
+
"description": "Userinfo runs to the last @, so a password containing @ doesn't leak into the host.",
|
|
244
|
+
"input": {
|
|
245
|
+
"message": "connect https://user:p@ss@host.example.com/x refused"
|
|
246
|
+
},
|
|
247
|
+
"expect": {
|
|
248
|
+
"skipReason": "Tier 3 provider error: connect https://host.example.com refused"
|
|
249
|
+
}
|
|
250
|
+
},
|
|
251
|
+
{
|
|
252
|
+
"id": "dict-repr-bearer-then-key",
|
|
253
|
+
"description": "The Bearer rule stops at the quote, so the next pair in a dict repr is still redacted.",
|
|
254
|
+
"input": {
|
|
255
|
+
"message": "{'Authorization': 'Bearer abcdefghijklmnop', 'X-Api-Key': 'zzzzzzzzzz'}"
|
|
256
|
+
},
|
|
257
|
+
"expect": {
|
|
258
|
+
"skipReason": "Tier 3 provider error: {'Authorization': '[redacted]', 'X-Api-Key': '[redacted]'}"
|
|
259
|
+
}
|
|
260
|
+
},
|
|
261
|
+
{
|
|
262
|
+
"id": "bearer-quoted-value",
|
|
263
|
+
"description": "A quoted Bearer value is redacted whole.",
|
|
264
|
+
"input": {
|
|
265
|
+
"message": "auth failed: Bearer 'abcdefghijklmnop' and Basic \"dXNlcjpwYXNzd29yZA==\""
|
|
266
|
+
},
|
|
267
|
+
"expect": {
|
|
268
|
+
"skipReason": "Tier 3 provider error: auth failed: Bearer [redacted] and Basic [redacted]"
|
|
269
|
+
}
|
|
270
|
+
},
|
|
271
|
+
{
|
|
272
|
+
"id": "bearer-embedded-and-escaped-quotes",
|
|
273
|
+
"description": "An unquoted Bearer value keeps embedded quotes; a quoted one keeps escaped quotes.",
|
|
274
|
+
"input": {
|
|
275
|
+
"message": "a: Bearer abc'defghijk rest; b: Basic \"abc\\\"defgh\" tail"
|
|
276
|
+
},
|
|
277
|
+
"expect": {
|
|
278
|
+
"skipReason": "Tier 3 provider error: a: Bearer [redacted] rest; b: Basic [redacted] tail"
|
|
279
|
+
}
|
|
280
|
+
},
|
|
281
|
+
{
|
|
282
|
+
"id": "bearer-brace-not-a-delimiter",
|
|
283
|
+
"description": "Only whitespace, a comma or the end stops an unquoted Bearer value, so text after a brace is redacted too.",
|
|
284
|
+
"input": {
|
|
285
|
+
"message": "auth failed: Bearer abc}secretvalue12 and Basic xyz)moresecret34"
|
|
286
|
+
},
|
|
287
|
+
"expect": {
|
|
288
|
+
"skipReason": "Tier 3 provider error: auth failed: Bearer [redacted] and Basic [redacted]"
|
|
289
|
+
}
|
|
240
290
|
}
|
|
241
291
|
]
|
|
242
292
|
}
|
|
@@ -102,7 +102,8 @@ class TestTier2Classifier:
|
|
|
102
102
|
|
|
103
103
|
def test_risk_levels(self):
|
|
104
104
|
assert self.classifier.get_risk_level(0.9) == "high"
|
|
105
|
-
assert self.classifier.get_risk_level(0.
|
|
105
|
+
assert self.classifier.get_risk_level(0.52) == "high" # the recalibrated boundary
|
|
106
|
+
assert self.classifier.get_risk_level(0.51) == "medium" # medium is [0.5, 0.52) with the bundled model
|
|
106
107
|
assert self.classifier.get_risk_level(0.3) == "low"
|
|
107
108
|
|
|
108
109
|
def test_is_injection(self):
|
|
@@ -119,7 +120,8 @@ class TestTier2ClassifierNoModel:
|
|
|
119
120
|
def test_risk_level_thresholds(self):
|
|
120
121
|
c = Tier2Classifier()
|
|
121
122
|
assert c.get_risk_level(0.9) == "high"
|
|
122
|
-
assert c.get_risk_level(0.
|
|
123
|
+
assert c.get_risk_level(0.52) == "high" # the recalibrated boundary
|
|
124
|
+
assert c.get_risk_level(0.51) == "medium" # medium is [0.5, 0.52) with the bundled model
|
|
123
125
|
assert c.get_risk_level(0.3) == "low"
|
|
124
126
|
assert c.get_risk_level(0.0) == "low"
|
|
125
127
|
assert c.get_risk_level(1.0) == "high"
|
|
@@ -39,7 +39,8 @@ class TestTier2ClassifierConfig:
|
|
|
39
39
|
def test_default_config(self):
|
|
40
40
|
c = Tier2Classifier()
|
|
41
41
|
assert c.get_risk_level(0.9) == "high"
|
|
42
|
-
assert c.get_risk_level(0.
|
|
42
|
+
assert c.get_risk_level(0.52) == "high" # the recalibrated boundary
|
|
43
|
+
assert c.get_risk_level(0.51) == "medium" # medium is [0.5, 0.52) with the bundled model
|
|
43
44
|
assert c.get_risk_level(0.3) == "low"
|
|
44
45
|
|
|
45
46
|
def test_custom_thresholds(self):
|
|
@@ -65,10 +66,10 @@ class TestTier2ClassifierConfig:
|
|
|
65
66
|
def test_get_config(self):
|
|
66
67
|
c = Tier2Classifier()
|
|
67
68
|
cfg = c.get_config()
|
|
68
|
-
# The bundled
|
|
69
|
-
#
|
|
70
|
-
#
|
|
71
|
-
assert cfg["high_risk_threshold"] == 0.
|
|
69
|
+
# The bundled model ships calibrated defaults via classifier_config.json
|
|
70
|
+
# (T=2.41, high_risk_threshold=0.52; ENG-3282). Library defaults still
|
|
71
|
+
# apply for fields the model doesn't override.
|
|
72
|
+
assert cfg["high_risk_threshold"] == 0.52
|
|
72
73
|
assert cfg["medium_risk_threshold"] == 0.5
|
|
73
74
|
assert cfg["min_text_length"] == 10
|
|
74
75
|
assert cfg["max_text_length"] == 10000
|
|
@@ -113,7 +114,7 @@ class TestCalibrationAutoLoad:
|
|
|
113
114
|
def test_thresholds_from_model_calibration(self):
|
|
114
115
|
c = Tier2Classifier()
|
|
115
116
|
cfg = c.get_config()
|
|
116
|
-
assert cfg["high_risk_threshold"] == 0.
|
|
117
|
+
assert cfg["high_risk_threshold"] == 0.52
|
|
117
118
|
|
|
118
119
|
def test_caller_overrides_model(self, tmp_path: Path):
|
|
119
120
|
# Caller-supplied config wins over model calibration.
|
|
@@ -13,15 +13,15 @@ wheels = [
|
|
|
13
13
|
|
|
14
14
|
[[package]]
|
|
15
15
|
name = "anyio"
|
|
16
|
-
version = "4.
|
|
16
|
+
version = "4.14.2"
|
|
17
17
|
source = { registry = "https://pypi.org/simple" }
|
|
18
18
|
dependencies = [
|
|
19
19
|
{ name = "idna" },
|
|
20
20
|
{ name = "typing-extensions", marker = "python_full_version < '3.13'" },
|
|
21
21
|
]
|
|
22
|
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
|
22
|
+
sdist = { url = "https://files.pythonhosted.org/packages/61/cc/a381afa6efea9f496eff839d4a6a1aed3bfafc7b3ab4b0d1b243a12573dd/anyio-4.14.2.tar.gz", hash = "sha256:cfa139f3ed1a23ee8f88a145ddb5ac7605b8bbfd8592baacd7ce3d8bb4313c7f", size = 260176, upload-time = "2026-07-12T20:29:07.082Z" }
|
|
23
23
|
wheels = [
|
|
24
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
24
|
+
{ url = "https://files.pythonhosted.org/packages/da/35/f2287558c17e29fafc8ef3daf819bb9834061cfa43bff8014f7df7f63bdc/anyio-4.14.2-py3-none-any.whl", hash = "sha256:9f505dda5ac9f0c8309b5e8bd445a8c2bf7246f3ce950121e45ea15bc41d1494", size = 125813, upload-time = "2026-07-12T20:29:05.763Z" },
|
|
25
25
|
]
|
|
26
26
|
|
|
27
27
|
[[package]]
|
|
@@ -175,11 +175,11 @@ wheels = [
|
|
|
175
175
|
|
|
176
176
|
[[package]]
|
|
177
177
|
name = "fsspec"
|
|
178
|
-
version = "2026.
|
|
178
|
+
version = "2026.6.0"
|
|
179
179
|
source = { registry = "https://pypi.org/simple" }
|
|
180
|
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
|
180
|
+
sdist = { url = "https://files.pythonhosted.org/packages/10/a1/ae4e3e5003468d6391d2c77b6fa1cd73bd5d13511d81c642d7b28ac90ed4/fsspec-2026.6.0.tar.gz", hash = "sha256:f5bac145310fe30e16e1471bd6840b2d990d609e872251d7e674241822abf01a", size = 313646, upload-time = "2026-06-16T01:57:28.105Z" }
|
|
181
181
|
wheels = [
|
|
182
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
182
|
+
{ url = "https://files.pythonhosted.org/packages/e5/22/4222d7ddf3da30f363edaa98e329c2bce6c65497c9cb2810931c8b2c0fbc/fsspec-2026.6.0-py3-none-any.whl", hash = "sha256:02e0b71817df9b2169dc30a16832045764def1191b43dcff5bb85bdee212d2a1", size = 203949, upload-time = "2026-06-16T01:57:26.358Z" },
|
|
183
183
|
]
|
|
184
184
|
|
|
185
185
|
[[package]]
|
|
@@ -646,7 +646,7 @@ wheels = [
|
|
|
646
646
|
|
|
647
647
|
[[package]]
|
|
648
648
|
name = "stackone-defender"
|
|
649
|
-
version = "0.9.
|
|
649
|
+
version = "0.9.3"
|
|
650
650
|
source = { editable = "." }
|
|
651
651
|
|
|
652
652
|
[package.optional-dependencies]
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{".":"0.9.2"}
|
stackone_defender-0.9.2/src/stackone_defender/models/minilm-multihead-v5/classifier_config.json
DELETED
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"model_id": "sentence-transformers/all-MiniLM-L6-v2",
|
|
3
|
-
"model_type": "minilm",
|
|
4
|
-
"use_seq_clf": false,
|
|
5
|
-
"hidden_size": 384,
|
|
6
|
-
"freeze_layers": 4,
|
|
7
|
-
"pooling": "mean",
|
|
8
|
-
"optimal_threshold": 0.4,
|
|
9
|
-
"datasets": [
|
|
10
|
-
"qualifire",
|
|
11
|
-
"jayavibhav",
|
|
12
|
-
"agentdojo",
|
|
13
|
-
"jasperls",
|
|
14
|
-
"jailbreakbench",
|
|
15
|
-
"toxic-chat",
|
|
16
|
-
"chatgpt-jailbreaks",
|
|
17
|
-
"email-hardneg",
|
|
18
|
-
"email-hardneg-gen",
|
|
19
|
-
"multilingual-hardneg",
|
|
20
|
-
"jailbreakbench-neg",
|
|
21
|
-
"toxic-chat-neg",
|
|
22
|
-
"fujitsu-injecagent",
|
|
23
|
-
"fujitsu-rag",
|
|
24
|
-
"enron-ham",
|
|
25
|
-
"connector-hardneg-v2",
|
|
26
|
-
"dev-tooling-hardneg-curated",
|
|
27
|
-
"dev-tooling-attacks",
|
|
28
|
-
"agentshield-shape-attacks",
|
|
29
|
-
"system-prompt-extraction-attacks",
|
|
30
|
-
"emoji-ci-benign",
|
|
31
|
-
"benign-user-queries",
|
|
32
|
-
"code-docs-benign"
|
|
33
|
-
],
|
|
34
|
-
"token_level": false,
|
|
35
|
-
"token_pool": "max",
|
|
36
|
-
"token_topk": 5,
|
|
37
|
-
"three_class": false,
|
|
38
|
-
"multi_head": true,
|
|
39
|
-
"aux_loss_alpha": 0.5,
|
|
40
|
-
"calibration": {
|
|
41
|
-
"temperatureT": 2.41,
|
|
42
|
-
"highRiskThreshold": 0.64,
|
|
43
|
-
"ece": 0.09,
|
|
44
|
-
"fitted_on": "labeled plugin events 2026-05-13",
|
|
45
|
-
"notes": "Raw highRiskThreshold 0.8 is math-equivalent to calibrated 0.64 at T=2.41. tier2Score is reported as calibrated probability (post-sigmoid(logit/T))."
|
|
46
|
-
}
|
|
47
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
defender-v0.9.2
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{stackone_defender-0.9.2 → stackone_defender-0.9.3}/src/stackone_defender/classifiers/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{stackone_defender-0.9.2 → stackone_defender-0.9.3}/src/stackone_defender/classifiers/patterns.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{stackone_defender-0.9.2 → stackone_defender-0.9.3}/src/stackone_defender/core/prompt_defense.py
RENAMED
|
File without changes
|
{stackone_defender-0.9.2 → stackone_defender-0.9.3}/src/stackone_defender/core/sentence_cleaner.py
RENAMED
|
File without changes
|
{stackone_defender-0.9.2 → stackone_defender-0.9.3}/src/stackone_defender/core/tier3_input.py
RENAMED
|
File without changes
|
{stackone_defender-0.9.2 → stackone_defender-0.9.3}/src/stackone_defender/core/tier3_policy.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{stackone_defender-0.9.2 → stackone_defender-0.9.3}/src/stackone_defender/sanitizers/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{stackone_defender-0.9.2 → stackone_defender-0.9.3}/src/stackone_defender/sanitizers/normalizer.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{stackone_defender-0.9.2 → stackone_defender-0.9.3}/src/stackone_defender/utils/field_detection.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{stackone_defender-0.9.2 → stackone_defender-0.9.3}/src/stackone_defender/utils/structure.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|