Security Audit Notes
This document tracks the current security hardening baseline for NeoOS Services and the checks that must pass before release.
September 2026 Trust-Boundary Re-audit
The current tree was re-audited rather than treating earlier issue notes as current fact. The findings below were live at the start of the remediation wave and are now closed in the paired implementation/tests:
- measured production code no longer compiles an ordinary Secrets Manager client for confidential masters; Recipient-attested KMS release is the only unwrap path;
- is one-shot, allowlisted, and refuses plaintext key/token overlays; temporary AWS credentials rotate only through ;
- X25519 and NeoDID KMS ciphertexts materialize in-memory as purpose/network envelopes during provision;
- signed compute/oracle/NeoDID lanes return 503 unless custody identity matches; HTTP 200 never carries a null signature or public key;
- raw signing uses dedicated raw-only keys and a distinct capability;
- production draws from (deterministic xorshift remains a test seam only);
- attested boundary JSON includes numeric ;
- production Cloudflare confidential execution requires exactly one origin;
- runtime credential domain separation is enforced by deploy checkers, the shared helper, production startup, and the Cloudflare control-plane auth path ();
- local/CI/EIF toolchain is Rust 1.98.0 and publish triggers watch ;
- dBFT reservations checkpoint PrepareRequest/PrepareResponse together, ignore view for Commit, key immutable public key + network magic, and reserve before signing.
The following earlier concerns were stale in the current tree and must not be reimplemented:
- fee ceilings already cover both system and network fees;
- rolling value and signature decisions are serialized across service clones, with audit/value/idempotency writes committed in one SQLite transaction;
- request-id completion is durable and exact-retry safe;
- transaction parsing already rejects negative/overflowing fees, trailing bytes, unknown attributes/opcodes, dynamic witness rules, and unaccounted script behavior.
Required resolved posture
- Measured production code accepts confidential material only as a KMS CiphertextBlob and unwraps it with a fresh Nitro Recipient attestation inside the enclave. The parent role has no , , , or plaintext-secret permission.
- is one-shot, uses an explicit allowlist, and rejects unknown names. , network, endpoints, custody routing, raw key material, feature/policy toggles, and trust anchors are image-owned. A separately authenticated route may rotate only temporary AWS access credentials.
- Production X25519 and NeoDID KMS plaintexts are purpose/network/version envelopes. The enclave validates and materializes them only in memory and marks their source internally as Nitro Recipient KMS. Remote confidential references remain explicit 503 until their authenticated claim transport is implemented.
- All signed response lanes fail 503 unless custody key identity, enabled state, network, and response identity match. HTTP 200 never contains a null signature or public key.
- Raw-only keys and callers are distinct from semantic transaction and consensus authorities.
- Security-sensitive random draws use ; deterministic closures remain test seams only.
- is present in key wire types and the attested boundary digest.
- Production images/units set ; confidential Cloudflare execution has one origin and no cross-origin retry; runtime/workflow/operator/ relayer/signing/relay/custody credentials are pairwise distinct via , deploy checkers, production web startup, and control-plane auth.
- Rust 1.98.0 is the single local, CI, and EIF toolchain, and publish triggers cover every Docker build input, including .
- dBFT v3.10.1 bodies are fully consumed and checkpointed by immutable public key, network magic, height, and safe phase semantics before signing. The claimed validator index must match an exact retry but cannot create a parallel slot.
Current Automated Coverage
- Root dependency audit is guarded by so known CityOfZion baseline findings stay explicit instead of silently expanding. The checker runs the active npm CLI through the current Node executable when is available (including Windows), uses a shell-backed npm fallback only for direct Windows invocation, and reports process-launch failures instead of misclassifying them as an empty audit.
- Repository verification runs script tests, control-plane tests, worker checks/tests, relayer checks/tests, web consistency checks, web unit tests, and web production build.
- Web security headers are defined in and covered by .
- Documentation navigation is covered by , including static docs routes and extended Markdown-backed pages.
- Contract artifact generation is validated by when and the pinned compiler are available.
Rust Advisory Disposition
currently reports RUSTSEC-2023-0071 for RustCrypto ; no patched release exists. The measured runtime uses RSA private operations only for a fresh, per-request RSA-2048 recipient key that unwraps one KMS response inside the enclave and is then dropped. The key is never persisted or reused, and the authenticated release flow generates a new key before each ciphertext, so a remote caller cannot collect the repeated timings for one long-lived key that the Marvin attack requires. Other call sites perform public-key verification only and are outside the advisory's private-key recovery path.Until RustCrypto publishes a constant-time release, the release check is:
The exception must be removed when a patched version exists, or earlier if an RSA private key is ever reused, persisted, or exposed through a remotely repeatable decryption/signing oracle.
is an unmaintained build-time macro inherited from Boa and retires with that compatibility engine; is the unmaintained target-specific dependency of AWS's Nitro NSM API. Neither warning currently identifies a vulnerability, but both remain migration debt rather than permanent exemptions.Manual Release Checklist
Before a production release, verify:
- passes in a clean checkout.
- The contract CI job installs the pinned Neo C# compiler and runs successfully.
- No secrets, private keys, service-role keys, tokens, or connection strings are committed or printed in logs.
- Production environment variables are scoped by network and deployment target.
- Callback, relayer, paymaster, and txproxy allowlists are reviewed against the intended deployment contracts.
- Any live smoke tests use dedicated testnet funds/accounts and do not reuse production signer material.
- reports no other vulnerability.
- , , and pass under Rust 1.98.0.
- , documentation coverage, worker/relayer/ops/script suites, custody-plane E2E, the NeoNexus real-process contract, and gitleaks pass.
- External-only blockers in are cleared or explicitly accepted by operators (key rotation, on-chain upgrades, host installs).
- Production preflight confirms pairwise-distinct runtime/workflow/operator/ relayer/signing/relay credentials and includes both signing and relay admin keys.
- The release's PCR measurements are rebuilt, committed, and pinned by the KMS key policy before ciphertext is provisioned.
Public API Route Policy
The following
routes are intentionally public and are hardened against enumeration and DoS abuse. They are rate-limited by (Upstash-backed when configured, in-memory otherwise) and enforce Zod input validation via .| Route | Authentication | Rate limit | Validation |
|---|---|---|---|
| Runtime/admin token required | 60 req/min | No unexpected query params | |
| Runtime/admin token required | 60 req/min | No unexpected query params | |
| Public (no token) | 60 req/min | No unexpected query params | |
| Runtime/admin token required | 30 req/min | and optional |
Authentication uses the shared runtime-token check in
(, , or presented as a Bearer token or header). Unauthenticated requests to protected routes receive ; malformed inputs receive ; rate-limited requests receive . Responses include , , and headers.Known Environment Caveat
Local developer machines that do not have
and installed will skip the contract compilation regression test. CI must still enforce contract compilation using the pinned compiler before release.Compatibility Child Process Hardening
The measured Rust EIF has no Node child or fallback engine. The separately published Node compatibility worker still spawns untrusted scripts and WASM modules in child processes. In that compatibility deployment the following settings must be enforced:
- The Node.js permission model is ON by default for script/WASM child processes. It can only be disabled by the explicit dev override , and that override is IGNORED when (fail closed). The retired toggle no longer has any effect.
- must contain specific hostnames or IP addresses. Wildcards (), , , and CIDR blocks are rejected at startup.
- and should be limited to the paths required by the workload.
- The Node.js permission model is applied automatically via in and .
Cron Diagnostic Policy
The
endpoint returns a generic response to unauthenticated callers. Diagnostic details (header presence, request metadata, and configuration checks) are logged server-side only and never included in the response body.