Podric commited on
Commit
80255f1
·
verified ·
1 Parent(s): 2f8db20

Prowl stage-3 multilingual secret encoder + card

Browse files
Files changed (5) hide show
  1. README.md +108 -0
  2. config.json +28 -0
  3. model.safetensors +3 -0
  4. tokenizer.json +0 -0
  5. tokenizer_config.json +15 -0
README.md ADDED
@@ -0,0 +1,108 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ license: apache-2.0
3
+ base_model: distilbert-base-multilingual-cased
4
+ pipeline_tag: text-classification
5
+ library_name: transformers
6
+ language:
7
+ - multilingual
8
+ - en
9
+ - de
10
+ - fr
11
+ - es
12
+ - ru
13
+ tags:
14
+ - security
15
+ - secret-detection
16
+ - credentials
17
+ - dlp
18
+ - code
19
+ metrics:
20
+ - f1
21
+ - precision
22
+ - recall
23
+ ---
24
+
25
+ # Prowl secret encoder
26
+
27
+ A multilingual text classifier that scores whether a fragment of text — a line of code, a config
28
+ value, a Jira comment, a log line, a chat message — **contains a leaked credential**. It is stage 3
29
+ of [Prowl](https://github.com/Lercas/prowl), a high-precision secret scanner, and is responsible for
30
+ the free-form and multilingual tail that regex and the linear model miss.
31
+
32
+ This model is **not a standalone scanner**. It is one stage of an ensemble; on its own it is a
33
+ recall booster, not a precision oracle. To scan a repo, use Prowl.
34
+
35
+ ## What it does
36
+
37
+ - **Input:** a short text span (≤512 tokens; the tool feeds ~128).
38
+ - **Output:** two logits → `softmax(...)[1]` is P(text contains a secret).
39
+ - **Decision:** fires at `P ≥ 0.90`, a threshold calibrated on a held-out validation split to
40
+ **precision ≥ 0.95** (value-disjoint from the benchmark — no leakage).
41
+
42
+ ## Role in the ensemble
43
+
44
+ Prowl combines three stages by union: a Go regex/checksum/entropy cascade, a char+word TF-IDF
45
+ logistic regression, and this encoder. Adding the encoder to the other two, measured on
46
+ [ProwlBench](https://github.com/Lercas/prowl/blob/main/benchmark/PROWLBENCH.md) (3,843 leakage-safe
47
+ cases):
48
+
49
+ | Configuration | Precision | Recall | F1 |
50
+ |---|:--:|:--:|:--:|
51
+ | cascade ∪ LR | 0.974 | 0.848 | 0.909 |
52
+ | **+ this encoder** | **0.971** | **0.970** | **0.970** |
53
+
54
+ The encoder lifts recall by 0.12 at a 0.003 precision cost. Against DeepPass2 (the prior
55
+ ML-for-secrets baseline) on the same cases, the full ensemble wins 1,815 disagreements and loses 36
56
+ — a **98.1% win rate** — and reaches recall 1.00 on German, French, Spanish, and Russian prose
57
+ passwords (DeepPass2: 0.75 / 0.87 / — / —).
58
+
59
+ Standalone recall at a fixed precision of 0.97, by channel:
60
+
61
+ | code | Jira | Confluence | log |
62
+ |:--:|:--:|:--:|:--:|
63
+ | 0.984 | 0.996 | 0.998 | 0.947 |
64
+
65
+ ## Usage
66
+
67
+ ```python
68
+ import torch
69
+ from transformers import AutoTokenizer, AutoModelForSequenceClassification
70
+
71
+ tok = AutoTokenizer.from_pretrained("Podric/prowl-secret-encoder")
72
+ model = AutoModelForSequenceClassification.from_pretrained("Podric/prowl-secret-encoder").eval()
73
+
74
+ def secret_score(text: str) -> float:
75
+ enc = tok(text, return_tensors="pt", truncation=True, max_length=128)
76
+ with torch.no_grad():
77
+ return torch.softmax(model(**enc).logits, -1)[0, 1].item()
78
+
79
+ secret_score('DB_PASSWORD = "tR4!nf0rce-2026-prod"') # high
80
+ secret_score("the deployment finished without errors") # low
81
+ # fire at >= 0.90
82
+ ```
83
+
84
+ ## Training
85
+
86
+ - **Base:** `distilbert-base-multilingual-cased` (104 languages, 6 layers, 134M params).
87
+ - **Objective:** binary sequence classification (secret-bearing / not).
88
+ - **Data:** the [Prowl secrets corpus](https://huggingface.co/datasets/Podric/prowl-secrets-corpus)
89
+ — 503k labeled records across code, tickets, logs, and prose. Real-secret sources (CredData, HF
90
+ PII sets) are held out by origin so the validation split is not a memorization check. Positives are
91
+ oversampled and real-origin records up-weighted.
92
+ - **Calibration:** the operating threshold is fixed post-hoc on validation to a precision target, not
93
+ learned, so it transfers to the ensemble's union rule.
94
+
95
+ ## Limitations
96
+
97
+ - **Binary, not typed.** It says *secret / not secret*; the secret **type** (AWS vs Stripe vs …)
98
+ comes from Prowl's cascade.
99
+ - **A stage, not a scanner.** High recall comes at a precision that only makes sense inside the
100
+ ensemble, where the cascade supplies structured-token precision. Do not deploy it alone as a gate.
101
+ - **Span-level, not token-level.** It flags a span; Prowl localizes the exact value.
102
+ - **Distilled size.** Chosen for CPU-friendly latency over a larger encoder; the few remaining misses
103
+ are ambiguous, label-noisy cases.
104
+
105
+ ## License
106
+
107
+ Apache-2.0, inherited from the base model. See the [Prowl repository](https://github.com/Lercas/prowl)
108
+ for the scanner (MIT) and the dataset card for data provenance and source licenses.
config.json ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "activation": "gelu",
3
+ "architectures": [
4
+ "DistilBertForSequenceClassification"
5
+ ],
6
+ "attention_dropout": 0.1,
7
+ "bos_token_id": null,
8
+ "dim": 768,
9
+ "dropout": 0.1,
10
+ "dtype": "float32",
11
+ "eos_token_id": null,
12
+ "hidden_dim": 3072,
13
+ "initializer_range": 0.02,
14
+ "max_position_embeddings": 512,
15
+ "model_type": "distilbert",
16
+ "n_heads": 12,
17
+ "n_layers": 6,
18
+ "output_past": true,
19
+ "pad_token_id": 0,
20
+ "qa_dropout": 0.1,
21
+ "seq_classif_dropout": 0.2,
22
+ "sinusoidal_pos_embds": false,
23
+ "tie_weights_": true,
24
+ "tie_word_embeddings": true,
25
+ "transformers_version": "5.12.1",
26
+ "use_cache": false,
27
+ "vocab_size": 119547
28
+ }
model.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:bbcec73040456f44c8a4b71fef61c9b8fc873f43c27c43d23a5fdf52f56ade32
3
+ size 541317368
tokenizer.json ADDED
The diff for this file is too large to render. See raw diff
 
tokenizer_config.json ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "backend": "tokenizers",
3
+ "cls_token": "[CLS]",
4
+ "do_lower_case": false,
5
+ "is_local": false,
6
+ "local_files_only": false,
7
+ "mask_token": "[MASK]",
8
+ "model_max_length": 512,
9
+ "pad_token": "[PAD]",
10
+ "sep_token": "[SEP]",
11
+ "strip_accents": null,
12
+ "tokenize_chinese_chars": true,
13
+ "tokenizer_class": "BertTokenizer",
14
+ "unk_token": "[UNK]"
15
+ }