{
  "$comment": "Single source of truth for static deny-lists exercised by scripts/run-tests.sh static profile. Adding/removing entries here is the gate code-review enforces (per AZ-482 constraint: 'deny-list lives in tests/security/banned-deps.json so additions are visible in code review'). Each section names the AC it traces to and is consumed by scripts/check-banned-deps.mjs.",
  "ml_libs": {
    "ac": "NFT-SEC-10",
    "scope": "package.json (dependencies + devDependencies)",
    "match": "regex-on-name",
    "patterns": [
      "onnxruntime",
      "tensorflow",
      "tflite",
      "coreml",
      "tfjs",
      "@tensorflow/",
      "@huggingface/",
      "transformers\\.js"
    ]
  },
  "signature_libs": {
    "ac": "NFT-SEC-11",
    "scope": "package.json (dependencies + devDependencies)",
    "match": "regex-on-name",
    "patterns": [
      "jsrsasign",
      "tweetnacl",
      "@noble/",
      "^jose$",
      "^jsonwebtoken$",
      "^node-forge$"
    ]
  },
  "persistence_libs": {
    "ac": "O2 (NFR) — no client-side persistence library",
    "scope": "package.json (dependencies + devDependencies)",
    "match": "regex-on-name",
    "patterns": [
      "^localforage$",
      "^idb$",
      "^dexie$"
    ]
  },
  "ws_graphql_ssr_libs": {
    "ac": "O11 (NFR) — no SSR/WS/GraphQL",
    "scope": "package.json (dependencies + devDependencies)",
    "match": "regex-on-name",
    "patterns": [
      "^ws$",
      "^socket\\.io$",
      "^graphql$",
      "^apollo$",
      "@apollo/",
      "^grpc-web$",
      "^react-dom/server$"
    ]
  },
  "legacy_integrations": {
    "ac": "NFT-SEC-13 — dropped legacy integrations not present in source",
    "scope": "src/ and mission-planner/ (production sources; tests excluded)",
    "match": "ripgrep-pattern",
    "patterns": [
      "WhatsApp",
      "TelegramBot",
      "D-Bus",
      "libsignal"
    ]
  },
  "concurrent_edit_patterns": {
    "ac": "NFT-SEC-14 (AC-N1 anti-criterion) — no concurrent-edit reconciliation surface",
    "scope": "src/ and mission-planner/ (production sources; tests excluded)",
    "match": "ripgrep-pattern",
    "patterns": [
      "concurrent.edit",
      "operational.transform",
      "crdt",
      "y-?websocket"
    ]
  },
  "owm_key_in_dist": {
    "ac": "NFT-SEC-09 (AC-1, dist/ portion) — OpenWeatherMap key not shipped in built bundle",
    "scope": "dist/ (post-`bun run build` artifacts)",
    "match": "literal",
    "patterns": [
      "335799082893fad97fa36118b131f919"
    ]
  },
  "owm_key_in_source": {
    "ac": "NFT-SEC-09 (AC-1, source portion) — OpenWeatherMap key not present in source tree",
    "scope": "src/ and mission-planner/ (production sources; tests excluded)",
    "match": "literal",
    "patterns": [
      "335799082893fad97fa36118b131f919"
    ]
  },
  "google_key_in_source": {
    "ac": "AZ-501 (F-SAST-1) — Google Geocode API key not present in source tree",
    "scope": "src/ and mission-planner/ (production sources; tests excluded)",
    "match": "literal",
    "patterns": [
      "AIzaSyAhvDeYukuyWVrQYbRhuv91bsi_jj5_Iys"
    ]
  },
  "alert_calls": {
    "ac": "NFT-SEC-07 (AZ-466 AC-5) — no alert() in production source",
    "scope": "src/ and mission-planner/ (production sources; tests excluded)",
    "match": "ripgrep-pattern",
    "patterns": [
      "\\balert\\s*\\("
    ],
    "$allowlist_comment": "Snapshot of currently-allowed alert() locations. Phase B feature tasks should drain this list one entry at a time. New alerts are blocked by the static check; removing an entry is a code-review-visible improvement.",
    "allowlist": [
      "src/features/annotations/MediaList.tsx",
      "src/features/flights/FlightsPage.tsx",
      "mission-planner/src/flightPlanning/JsonEditorDialog.tsx",
      "mission-planner/src/flightPlanning/flightPlan.tsx"
    ]
  },
  "destructive_surfaces": {
    "ac": "NFT-SEC-08 (AZ-466 AC-4) — every destructive surface is reviewed and either gated by ConfirmDialog or recorded as a known drift",
    "scope": "src/ files that call api.delete( or destructive api.patch(",
    "match": "file-level: a file containing a destructive call MUST be listed below; new destructive surfaces FAIL the check",
    "patterns": [
      "api\\.delete\\(",
      "api\\.patch\\([^,]+,\\s*\\{\\s*isActive\\s*:"
    ],
    "$gated_comment": "Files that perform destructive mutations AND wire ConfirmDialog around them. Code review checks the wiring per file.",
    "gated": [
      "src/features/annotations/MediaList.tsx",
      "src/features/flights/FlightsPage.tsx"
    ],
    "$drift_comment": "Files that perform destructive mutations WITHOUT a ConfirmDialog gate today. Phase B follow-up tasks land the gate and move each entry to `gated`. Adding a new entry here requires a code-review reason.",
    "drift": [
      "src/features/admin/AdminPage.tsx"
    ]
  }
}
