Files
ui/_docs/02_document/tests/security-tests.md
T
Oleksandr Bezdieniezhnykh 510df68bcf [AZ-447] autodev Steps 1-4 baseline: docs, tests, refactor specs
Captures the full output of autodev existing-code Phase A through
Step 4 (Code Testability Revision) for the Azaion UI workspace:

- Step 1 Document: _docs/02_document/ (FINAL_report, architecture,
  glossary, components/, modules/, diagrams/, system-flows,
  module-layout) plus _docs/00_problem/ + _docs/01_solution/ +
  _docs/legacy/ + _docs/how_to_test + README.
- Step 2 Architecture Baseline: architecture_compliance_baseline.md.
- Step 3 Test Spec: _docs/02_document/tests/ (environment,
  test-data, blackbox/performance/resilience/security/
  resource-limit tests, traceability-matrix), enum_spec_snapshot,
  expected_results/results_report.md (98 rows), plus the
  run-tests.sh + run-performance-tests.sh runners.
- Step 4 Code Testability Revision: 01-testability-refactoring/
  run dir (list-of-changes C01-C07, deferred_to_refactor,
  analysis/research_findings + refactoring_roadmap) and the 7
  child task specs AZ-448..AZ-454 under _docs/02_tasks/todo/
  plus _dependencies_table.md.
- _docs/_autodev_state.md pins the cursor at Step 4 / refactor
  Phase 4 entry so /autodev resumes cleanly.

Epic AZ-447 (UI testability gates) tracks the 7 child tasks that
will land in subsequent commits.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-11 00:38:49 +03:00

8.2 KiB

Security Tests

Blackbox security assertions against the SPA's observable surface: token storage discipline, refresh cookie attributes, RBAC route gating, credentials flag, secrets-in-source checks, destructive-action policy, dependency hygiene. These complement the server's RBAC and the suite's security_approach (_docs/00_problem/security_approach.md); they do NOT replace server-side enforcement (O4).

NFT-SEC-01: Bearer is never written to localStorage or sessionStorage

Traces to: AC-02, O2 Profile: static + e2e

Steps:

Step Consumer Action Expected Response
1 Static code-search on src/ and mission-planner/src/ for `localStorage. sessionStorage.nearbearer
2 E2E: complete a login; inspect localStorage and sessionStorage keys neither contains the bearer value

Pass criteria: row 04 — match_count == 0; runtime storage does not contain the bearer. Expected result source: results_report.md row 04.


NFT-SEC-02: document.cookie does not expose the refresh token

Traces to: AC-03 Profile: e2e + static

Steps:

Step Consumer Action Expected Response
1 Static code-search for document.cookie reads against `refreshToken refresh-cookie`
2 E2E: complete login; read document.cookie from page context returned string does NOT contain the refresh-token value (row 06)

Pass criteria: rows 05 + 06. Expected result source: results_report.md rows 05, 06.


Traces to: AC-03, E3, O5 Profile: e2e

Steps:

Step Consumer Action Expected Response
1 Login via POST /api/admin/auth/login against the suite stack Set-Cookie header returned
2 Inspect header value matches regex Secure;.*HttpOnly;.*SameSite=Strict (case-insensitive, attribute-order-tolerant)

Pass criteria: row 07 — regex match. Notes: this is a server-contract assertion; the UI test exists as defence-in-depth so a suite regression is caught before it lands in production. Expected result source: results_report.md row 07.


NFT-SEC-04: credentials: 'include' is set on every authenticated fetch

Traces to: AC-01, O3 Profile: fast (apiClient wrapper) + e2e (live capture)

Steps:

Step Consumer Action Expected Response
1 Issue an authenticated request via apiClient RequestInit captured
2 Inspect credentials === 'include' (row 01)
3 Repeat for the bootstrap refresh same (row 02 — quarantined until Step 4 bootstrap fix)

Pass criteria: rows 01 + 02. Expected result source: results_report.md rows 01, 02.


NFT-SEC-05: /admin route blocks non-admins client-side (defence in depth)

Traces to: AC-22 Profile: e2e — quarantined until role-gate is added (Step 4 / Step 8)

Steps:

Step Consumer Action Expected Response
1 Log in as op_alice (Operator, no admin role) session active
2 Navigate to /admin URL changes
3 Inspect final URL + DOM URL is /flights; <AdminPage> NOT mounted

Pass criteria: row 08. Notes: server-side RBAC is authoritative; the UI gate is a usability + leakage layer. Expected result source: results_report.md row 08.


NFT-SEC-06: /settings route gate is applied per RBAC

Traces to: AC-22 Profile: e2e — quarantined

Steps:

Step Consumer Action Expected Response
1 Log in as user without SETTINGS permission session active
2 Navigate to /settings URL changes
3 Inspect URL is /flights; <SettingsPage> NOT mounted

Pass criteria: row 10. Expected result source: results_report.md row 10.


NFT-SEC-07: alert() is forbidden anywhere in the SPA

Traces to: AC-14, O10 Profile: static

Steps:

Step Consumer Action Expected Response
1 Regex sweep src/ and mission-planner/src/ for \balert\( match_count == 0

Pass criteria: row 50. Expected result source: results_report.md row 50.


NFT-SEC-08: ConfirmDialog gates every destructive action

Traces to: AC-14, AC-30, O10 Profile: fast

Steps:

Step Consumer Action Expected Response
1 For each destructive surface in _docs/ui_design/ (class delete, user deactivate, dataset bulk-overwrite, etc.) sequence checked
2 Confirm sequence on click → before any HTTP fires dialog present (row 51)
3 On Confirm in class-delete flow → exactly one DELETE to ^/api/admin/classes/[0-9]+$ (row 49)

Pass criteria: rows 49 + 51. Expected result source: results_report.md rows 49, 51.


NFT-SEC-09: OpenWeatherMap API key is not shipped in source or bundle

Traces to: AC-20, P10 Profile: static (source) + static (bundle)

Steps:

Step Consumer Action Expected Response
1 Regex sweep src/ and mission-planner/src/ for the literal current OWM key value match_count == 0 (row 63)
2 Regex sweep for appid= and api_key= literal occurrences in source URLs match_count == 0 (row 63)
3 Scan dist/**/*.js post-build for the literal key match_count == 0 (Phase 3 may downgrade to "until Step 4 fix")

Pass criteria: row 63. Status: quarantined for source check until Step 4 fix; the bundle-scan check passes immediately for src/ (mission-planner not bundled, AC-31). Expected result source: results_report.md row 63.


NFT-SEC-10: No in-browser ML libs

Traces to: AC-N2 Profile: static

Steps:

Step Consumer Action Expected Response
1 Parse package.json and mission-planner/package.json dependencies dependency lists
2 Match against `^(onnxruntime @?tensorflow(?:js)?(?:/.*)?

Pass criteria: row 92. Expected result source: results_report.md row 92.


NFT-SEC-11: No response-signature / JOSE libs on the request path

Traces to: AC-N4 Profile: static

Steps:

Step Consumer Action Expected Response
1 Parse package.json dependencies list
2 Match against `^(jsrsasign tweetnacl

Pass criteria: row 94. Expected result source: results_report.md row 94.


NFT-SEC-12: No service worker — offline mode is explicitly absent

Traces to: AC-N3 Profile: e2e

Steps:

Step Consumer Action Expected Response
1 Load the SPA in a fresh browser context app boots
2 Read navigator.serviceWorker.getRegistrations() empty array

Pass criteria: row 93 — no service worker registered. Expected result source: results_report.md row 93.


NFT-SEC-13: Dropped legacy features are not present in source

Traces to: AC-N5 Profile: static

Steps:

Step Consumer Action Expected Response
1 Regex sweep src/ and mission-planner/src/ for `SoundDetections DroneMaintenance`

Pass criteria: row 95. Expected result source: results_report.md row 95.


NFT-SEC-14: Anti-criterion AC-N1 — no concurrent-edit reconciliation surfaces

Traces to: AC-N1 Profile: e2e + static

Steps:

Step Consumer Action Expected Response
1 Open the same annotation in two browser sessions; edit both both save individually
2 Inspect each session's DOM no merge UI; no presence indicator

Pass criteria: row 91. Notes: this is an anti-criterion — the test enforces that the feature is NOT silently added. Expected result source: results_report.md row 91.