Project Simurgh is a research prototype. Security fixes are applied to the latest tagged release and the main branch.
| Version | Supported |
|---|---|
v0.4.16-stage-2-8C-8D-linux-wayland-systemd-ci |
✅ Active |
v0.4.13-stage-2-6-2-7-closeout (Windows Device Shield closeout) |
✅ Active |
v0.4.13 (Stage 2.7 cross-platform unification) |
✅ Active |
v0.4.12 (Stage 2.6B Windows scanner validation) |
✅ Active |
v0.4.11 (Stage 2.6 Windows scanner branch) |
✅ Active |
v0.4.7 (Stage 2.5 macOS scanner) |
✅ Active |
v0.4.6 (Stage 2.4 SDK/lifecycle) |
✅ Active |
v0.4.5 (Stage 2.3 daemon foundation) |
✅ Active |
v0.4.3 (Stage 2 hardening) |
✅ Active |
v0.4.2 (Stage 2.2 macOS node pairing) |
✅ Active |
v0.4.1 (Stage 2.1 macOS integrity) |
✅ Active |
v0.3.x (Stage 1 / 1.5) |
|
main (development) |
✅ Active |
| Earlier tags | Not maintained |
Do not open a public GitHub issue for security vulnerabilities.
Report security issues to: raoof.r12@gmail.com
Include:
- Description of the vulnerability
- Steps to reproduce
- Affected component (server, helper, audit chain, dashboard, etc.)
- Potential impact assessment
You will receive a response within 72 hours. If the vulnerability is confirmed, a fix will be prioritised for the next release. You will be credited in the changelog unless you request anonymity.
The trust-boundary table below describes the Stage 1 surface. Stage 2.1 added an Ed25519-signed integrity-proof envelope (
/api/integrity/proofs); Stage 2.2 added per-session node pairing (/api/integrity/pairing/{challenge,complete}); v0.4.3 added rate limiting on the proofs route, cryptographically-reconciled audit hints (safeParsedPairingHints), and a constant-time challenge compare. Stage 2.3 adds a localhost daemon proof surface (/api/device/{challenge,pair}plus telemetrydaemon_proof) with P-256 signatures. Stage 2.4 moves the browser bridge into a reusable SDK. Stage 2.5 adds a CoreGraphics-backed, metadata-only macOS scanner summary inside signed daemon proofs. Stage 2.6B validates live WindowsGetWindowDisplayAffinitydetection on Windows 10 Pro build 19045 using a controlled local fixture. This still does not constitute hardware attestation, notarised distribution, MDM/Intune readiness, Windows Service readiness, kernel-level visibility, or a production device-trust claim.
- Daemon binds to
127.0.0.1only. - Browser-facing daemon endpoints reject unknown origins and require
X-Simurgh-Local-Client: browseron POST requests. - Server challenges expire after 30 seconds and are single-use.
SIMURGH_REQUIRE_DAEMON=trueenforces signeddaemon_proofon telemetry; missing proofs are rejected and HMAC-audited asDAEMON_MISSING.- Server stores public key hashes, proof ages, daemon state, signature status, and capture-excluded counts only.
- Raw process names, raw window titles, usernames, serial numbers, MAC addresses, screenshots, pixels, audio, typed content, and pasted content remain forbidden.
public/sdk/simurgh-browser-sdk.jsowns daemon discovery, pairing, proof fetch, telemetry send, hardened missing-proof handling, and client daemon state.- SDK state is explicit:
idle,discovering,available,pairing,paired,proof_ready,missing,stale,untrusted, anderror. - Server-side proof replay or invalid-proof responses move the client state to
untrusted; hardened missing-proof mode blocks telemetry before spoofing a daemon proof. simurgh-daemon doctorreports only status labels such as daemon reachability, port availability, Keychain identity presence, allowed-origin configuration, localhost binding, server reachability, and proof round-trip readiness.- Development LaunchAgent scripts are local-only and user-scoped. They do not install into system LaunchDaemons and do not make production, notarisation, or managed-deployment claims.
AffinityScanneruses CoreGraphics window metadata only and filters for meaningful onscreen windows before counting capture-excluded risk.- Scanner summaries are signed inside daemon proofs; browser code cannot append trusted scanner fields beside the proof.
- Server validation rejects forbidden raw local fields including process/window names, raw process/window fields, PIDs, usernames, home directories, file paths, serial numbers, MAC addresses, screenshots, pixels, audio, typed content, and pasted content.
scanner_unavailableandpermission_deniedare accepted as signed scanner states and treated as warning/manual-review context, not automatic findings.
- Windows scanner fields are accepted only inside signed daemon proofs with
platform: "windows"andscanner_version: "2.6.0". WDA_EXCLUDEFROMCAPTUREmaps to Critical/manual review throughcapture_excluded_window_count > 0.WDA_MONITORmaps to Warning/manual review throughmonitor_only_window_count > 0andcapture_restricted_window_count > 0.- Tampered scanner counts invalidate the P-256 daemon proof signature; replayed proof challenges are rejected.
- Raw HWNDs, PIDs, process names, window titles, executable paths, usernames, home directories, screenshots, pixels, webcam frames, microphone audio, typed content, and pasted content are forbidden and rejected recursively with the generic
forbidden_local_fieldreason. - Real-device validation on Windows 10 Pro build 19045 confirmed normal scans,
WDA_MONITOR,WDA_EXCLUDEFROMCAPTURE, signed proof acceptance, tamper/replay rejection, report/dashboard output, audit verification, and privacy audit.
The Windows Device Shield path is protected by:
- Signed P-256 daemon proofs with session/exam/challenge binding
- Single-use challenge replay protection (consumed on use)
- Timestamp freshness checks (±30 s past, +5 s future)
- Platform and all scanner-field content is inside the signed canonical payload
- Recursive forbidden local-field rejection (
containsForbiddenLocalFieldDeep) - Generic
forbidden_local_fieldreason (no raw identifier ever echoed) - Metadata-only scanner output (counts only, no raw identifiers)
- Pairing-level unsupported-platform rejection (
unsupported_platformat both pairing and proof layers) - Report/dashboard/audit privacy minimisation
- Controlled local fixture validation (
SimurghAffinityFixture) - Stage 2.6/2.7 closeout cybersecurity audit (24/24 tests across nine dimensions)
The Windows daemon is not a production Windows Service. It is not MDM/Intune managed. It does not provide hardware attestation, kernel visibility, or endpoint-control guarantees.
| Component | Trust Level | Mechanism |
|---|---|---|
| Student browser | Untrusted | Strict allowlist + range-reject validation; replay guard (sequence + timestamp); rate limit |
| Joined student session | Token-bound | HMAC-SHA256 session token issued at /api/exams/:id/join, required for lifecycle + telemetry |
| Native helper | Authenticated | x-simurgh-helper-secret shared-secret header + per-helper rate limit |
| Instructor dashboard | Authenticated | Bearer token (SIMURGH_INSTRUCTOR_TOKEN) — query-string token stripped from URL after capture |
| Claude API | Trusted service | Receives sanitised behavioural metadata only, never raw content |
| Audit chain | Tamper-evident | HMAC-SHA256 linked entries; any modification invalidates downstream signatures |
Four independent secrets in production. Reuse is not permitted.
| Secret | Purpose |
|---|---|
SIMURGH_INSTRUCTOR_TOKEN |
Dashboard, sessions list, report, audit verify, SSE |
SIMURGH_HELPER_SECRET |
Native helper authentication |
SIMURGH_AUDIT_SECRET |
HMAC key for the audit chain |
SIMURGH_SESSION_SIGNING_SECRET |
HMAC key for student session tokens |
The server refuses to start in non-demo mode if any of these are unset.
Simurgh is designed around data minimisation. The following data is never collected, stored, logged, or transmitted to third parties:
- Screen pixels, screenshots, or screen recordings
- Webcam frames or microphone audio
- Typed answer content
- Paste content (only paste length and count are recorded)
- Raw student names or email addresses (SHA-256 hashed at ingress only)
- Biometric identifiers
- Raw process names or window titles (Stage 1 helper hashes these; Stage 2+ daemon proofs reject them unconditionally as
forbidden_local_field— no flag enables raw transmission)
Enforcement points:
src/privacy/privacyConfig.js— declarative allowlist of what may be collectedsrc/privacy/normaliseTelemetry.js— strict allowlist applied before storagesrc/privacy/hashIdentity.js— one-way SHA-256 hashing at point of entrytools/privacy-audit.mjs— CI-ready scanner that fails if forbidden fields appear in generated data
These controls cannot be bypassed by configuration alone — modification requires a code change visible in the audit log.
Simurgh produces risk scores and event timelines. It never automatically accuses, flags, or penalises a student. Every anomaly recommendation is worded as:
"Manual review required. No automatic misconduct finding."
This is the canonical wording emitted by src/academic/riskScoring.js for Warning and Critical verdicts. Institutions deploying Simurgh must apply human judgment and due process before any action is taken on the basis of a risk score.
The following attack classes are not detectable from telemetry alone:
- Click-through overlays —
WS_EX_TRANSPARENT(Windows) orignoresMouseEvents(macOS) do not fire focus or paste events - Read-don't-paste workflows — silent transcription at human WPM with no paste events
- GPU-layer overlays (DirectX / Metal hooks, e.g. Cluely-class) — bypass both DOM events and
getDisplayMedia()
Mitigations:
- The macOS
simurgh-helper(Countermeasure A) enumerates display-affinity flags at the OS level and triggers a Critical override when a capture-excluded window is detected. - Stage 4 research will explore hardware-rooted attestation and on-device verification for GPU-layer overlays.
These limitations are documented openly. The system is privacy-preserving, tamper-evident, hardened, and auditable against the Stage 1 threat model — it is not unbreakable, and Simurgh's value proposition does not rely on the claim that it is.
npm auditThe repository currently reports 0 known vulnerabilities. Report any new findings with high or critical severity via the vulnerability disclosure process above.
- P-256 ECDSA daemon proof signed over canonical JSON; server verifies signature before processing any telemetry.
- Challenge binding: proof includes a server-issued challenge echoed back; session-scoped one-time use prevents replay.
- Wayland portal probe uses property reads only (
AvailableSourceTypes); never callsCreateSession,SelectSources,Start, orOpenPipeWireRemote. Enforced by source-grep tests in both the Wayland scanner test file and the cybersecurity audit. display_serveris locked to the first verified value per session; mid-session changes are rejected withdisplay_server_mismatchand emitDAEMON_PROOF_REJECTEDto the HMAC audit chain.- XWayland output never claims
x11_fullorwayland_limitedcoverage — alwaysxwayland_partial. browser_package_hintis UX-only. The server (server.js), proof validator (daemonProof.js), schema (platformScannerSchema.js), risk policy (scannerRiskPolicy.js), and report builder (reportBuilder.js) all source-grep clean of the field.- systemd
--userunit is development-only. No system-wide service. No root. No sudo in lifecycle scripts. Hardening directives:NoNewPrivileges=true,ProtectSystem=strict,ProtectHome=read-only,PrivateTmp=true. - Ubuntu CI now enforces Rust
cargo fmt --check,cargo clippy -- -D warnings,cargo test, andshellcheckon the lifecycle scripts. Xvfb integration tests are mandatory viaSIMURGH_REQUIRE_XVFB_TESTS=1. - Forbidden raw local field rejection: server and proof validator reject any proof payload carrying forbidden local identifiers (window titles, PIDs, process names, usernames, home paths).
- No automatic misconduct finding: all anomalies are flagged for manual review only; the system makes no misconduct determination.
Non-claims preserved: research prototype only. No production Linux endpoint deployment, no distro packaging, no system-wide service, no MDM, no hardware attestation, no kernel-level visibility, no universal Wayland surface enumeration, no GPU overlay detection, no automatic misconduct detection.
See docs/STAGE_2_8_LINUX_TECHNICAL_BRIEF.md for the full security architecture.
npm test # full unit suite
node tools/privacy-audit.mjs # scan generated data for forbidden fields
node tools/verify-audit.mjs <chain.json> # verify an exported HMAC audit chain