SentinelAI — Detection Rules (MVP)
The MVP classifier is 100% deterministic rules + regex — no AI/LLM in the decision path.
Everything runs locally in the endpoint agent (agent/app/classifier.py + risk.py) in <5 ms.
This document is the authoritative list of what is detected and how a decision is reached.
Phase 2 adds an optional AI verification layer to reduce false positives (see
docs/AI.md). It is off by default; the rules below are always the baseline.
1. What is detected
PII
| Rule | Pattern (plain English) | Severity | Validation |
|---|---|---|---|
email_address |
name@domain.tld |
20 | — |
phone_number |
US-style phone numbers | 25 | — |
us_ssn |
123-45-6789 |
80 | — |
credit_card |
13–16 digit card numbers | 85 | Luhn checksum (rejects random digits) |
Secrets & credentials (severity 95 — always block-worthy)
| Rule | Detects |
|---|---|
aws_access_key |
AKIA… access key IDs |
aws_secret |
aws_secret_access_key = … (40-char) |
openai_key |
sk-… API keys |
github_token |
ghp_/gho_/ghu_/ghs_/ghr_… |
google_api_key |
AIza… |
google_oauth_secret |
GOCSPX-… |
stripe_secret_key |
sk_live_… / rk_live_… |
slack_token |
xoxb-/xoxp-/xoxa-/xoxr-/xoxs-… |
slack_webhook |
https://hooks.slack.com/services/… |
azure_storage_key |
AccountKey=… connection strings |
npm_token |
npm_… (36-char) |
private_key |
-----BEGIN … PRIVATE KEY----- |
jwt |
eyJ….….… JSON Web Tokens |
bearer |
Bearer <token> |
password_assignment |
password/passwd/pwd/secret = … |
Source code (severity 55)
Fires when ≥2 code signals appear together (reduces false positives on prose):
def/class …, function/const/let/var …, import …, from … import …, #include <…>,
Java signatures (public static void …), SELECT … FROM …, and operators/markers
(=>, ::, console.log(, println!(, System.out.print).
Financial (severity 45, IBAN 70)
financial_terms— revenue, EBITDA, salary, compensation, invoice, routing/account number, net income, forecast, P&L, gross margin, …iban— International Bank Account Numbers, validated with the ISO mod-97 checksum.
Contract / legal (severity 45)
contract_language — confidential(ity), NDA, non-disclosure, master service agreement,
indemnif…, "hereinafter", termination clause, governing law, "whereas", …
2. How a decision is made (risk.py)
Base score = the single highest-severity finding, plus a volume bump (
+3per additional match, capped at+15).Destination weight multiplies the score by where the prompt is going:
Destination Weight Rationale ChatGPT / Claude / Gemini ×1.0 consumer, highest exfil risk Microsoft Copilot ×0.85 M365 Copilot ×0.70 enterprise tenant, lower default risk Bands map the final 0–100 score to an action:
Score Decision Label 81–100 block + alert SOC Restricted 51–80 block Confidential 26–50 warn (coaching, proceed on ack) Internal 0–25 allow (log only) Public
Worked example: prompt containing an sk-… key sent to ChatGPT →
severity 95, ×1.0, +volume ⇒ 98 → block / Restricted.
3. Known limitations (be upfront with stakeholders)
- Structured data only. Regex excels at keys/PII/patterns; it cannot catch semantic secrets with no fixed shape — e.g. "our Q3 acquisition target is Acme for $40M." That is the gap the Phase-2 AI layer addresses.
- No context. A public code sample and proprietary source both trip
source_code. The AI layer is designed to suppress exactly these false positives (never to add detections). - Static thresholds. Bands and destination weights are hardcoded defaults; per-org policy is an open decision (Clarification #6).
- Maintenance. Vendor token formats change; patterns need periodic review.
4. Extending
Add a rule in agent/app/classifier.py (a (name, regex) pair in _SECRETS, or a new detector
in classify()), pick a severity, and add a test in agent/tests/test_classifier.py. Keep
high-false-positive patterns gated behind a checksum (see _luhn_ok, _iban_ok) where possible.