πŸ›‘οΈ SentinelAISecurity Analyst Portal

SentinelAI β€” Detection Rules

Ruleset version: 2026.09.1  Β·  23 rules across 5 categories.

The 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 human-readable list; the live source of truth is the running agent:

curl http://127.0.0.1:8787/health   # β†’ { … "rules_version": "2026.09.1" }
curl http://127.0.0.1:8787/rules    # β†’ { rules_version, rules: [ {category, name, severity}, … ] }

Rules live in code (deterministic and versioned by design); changing them is a code change + a RULES_VERSION bump, not customer config. Org/department tailoring is config (policy.json) β€” see docs/TAILORING.md.

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)

  1. Base score = the single highest-severity finding, plus a volume bump (+3 per additional match, capped at +15).

  2. 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
  3. 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.

Decision flow

Prompt text (pre-send) Rules scan β€” regex + checksums Luhn (cards) Β· mod-97 (IBAN) Β· β‰₯2 signals (code) Any findings? no allow yes Base = highest severity (0–95) + volume bump (+3 / extra match, capped +15) Γ— destination weight ChatGPT/Claude/Gemini 1.0 Β· Copilot 0.85 Β· M365 0.70 Final score 0–100 81–100block + alert SOC β€” Restricted 51–80block β€” Confidential 26–50warn β€” Internal 0–25allow β€” Public

Secrets/PII always land in the block bands via rules. The optional AI layer sits after this β€” it can suppress a fuzzy-category block (β†’ allow) or raise an allow it judges sensitive (β†’ block); see docs/AI.md.


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.