Skip to content

fix(security): confine approved writes and bound retained refusals - #120

Merged
aien-spark merged 1 commit into
mainfrom
fix/security-workspace-and-session-bounds
Oct 26, 2026
Merged

aien-spark merged 1 commit into
mainfrom
fix/security-workspace-and-session-bounds

Conversation

@aien-spark

Copy link
Copy Markdown
Collaborator

Scope and source

Fixes SECNET-01 and SECNET-05 from the network security review, plus S3/S5 workflow changes supplied by the security workflow owners. Based on origin/main d868d9c. Draft only; no merge, deployment, endpoint probing, GPU or live-service changes.

SECNET-01: approved workspace write confinement

  • Pin the enrolled workspace directory descriptor; resolve the target parent with Linux openat2 BENEATH|NO_SYMLINKS.
  • Refuse non-regular final entries, create a new inode exclusively and atomically replace the target name through that descriptor. No final symlink or hard link is written through; unsupported platforms/kernels fail closed.
  • rustix 1.1.5 is explicitly documented as a host syscall binding, with the intended Omega replacement boundary. Existing AIEN approval authority is unchanged.
  • Before: nice -n 19 cargo test --offline -j2 --test approval approved_write_rejects from adapters/aien exited 101: dangling symlink created the outside file, and a link swapped after approval overwrote outside original with must stay inside.
  • After: both regressions pass. Positive: approved_effect_executes_once writes the exact approved bytes. Negative controls: mismatched/expired grants and workspace escapes remain refused.

SECNET-05: bounded retained model-call state

  • Automatic result inputs now count toward max_inputs_per_trace; all refusal events count toward new host-set max_events_per_trace (default 4096).
  • Reserve the worst-case retained output before runtime work. A saturated held trace closes, so no effects run with a truncated exposure ledger. Closed trace retirement/replay protections remain intact.
  • Bound AIEN adapter lifetime effect correlation/exposure state to 4096 distinct effect ids; further ids fail closed rather than growing past pipeline close cycles. Existing approvals/replays and reads remain available. Hosts must retire the adapter after resolving outstanding approvals to obtain fresh capacity.
  • Also corrected a reproduced stale-effect bug discovered in review: a read reusing another trace's request id no longer consumes its reserved write.
  • Before: rejected_turns_do_not_overflow_automatic_inputs exited 101 on the third retained result; replay_refusals_do_not_grow_retained_events exited 101 after unbounded repeated replay events. The pending_effect_metadata_has_a_fail_closed_capacity regression exited 101 (RequiresApproval instead of Denied for a further id), and read_does_not_consume_another_traces_reserved_write exited 101 after unexpectedly changing the file.
  • After: regressions pass, repeated malformed/duplicate turns remain bounded, and the saturated-turn control proves zero decide/execute calls. Existing valid read, write, approval continuation, replay and trace-limit controls pass.
  • Read-only boundary review identified that reservation must include up to four turn metadata events; the implementation includes that correction and preserves the existing max_traces refusal text.

Local offline verification actually run

All builds use nice -n 19 and -j2; no endpoint or device work.

  • nice -n 19 cargo fmt --all in rust and adapter workspaces completed.
  • nice -n 19 cargo test --offline -j2 -p interplane-crossveil from rust: unit test 1 passed; pipeline 21 passed; refusal_exposure 9 passed; session_limits 20 passed; doc-tests 0 tests. Exit 0.
  • nice -n 19 cargo test --offline -j2 --test approval --test pipeline from adapters/aien: approval 19 passed; pipeline 14 passed. Exit 0. Pinned aegis dependency warned that Mojo was absent and skipped its SIMD build; these tests exercised the actual Rust filesystem/approval paths, not SIMD/model qualification.
  • Full workspace gates, hosted Actions, live daemon integration, model/backend qualification and GPU tests NOT_RUN.

S3: isolate the pinned verifier build (workflow-owner evidence)

Move pinned aien-test source outside the candidate before Cargo runs, use an explicit trusted target directory, and cache registry/git dependencies only under a new isolated-v1 key. No target executable cache is reused.

Offline shell replay on cargo 1.98.1 / rustc 1.98.1, dependency-free aien-test fixture, both checks/alpha jobs: candidate .cargo/config.toml points build.rustc-wrapper at a marker-writing exit-87 shell probe and redirects build.target-dir. Before exact workflow build shell: cargo exit 101, marker created, no installed binary. After exact move/build shell: exit 0, marker absent, installed fixture prints TRUSTED-FIXTURE, output exists only at RUNNER_TEMP/aien-test-target, candidate poison-target absent. Negative: same probe config inside trusted source .cargo/config.toml: exit 101, marker created, no installed binary. CARGO_NET_OFFLINE=true, CARGO_BUILD_JOBS=2, nice -n19. Fixture only, not a full pinned verifier build or Actions/Alpha qualification.

Matching pending guide update: aien-dev/omega-harness#19 . The SECNET implementation changes do not alter Omega compiler/language/host-word guidance.

S5: manual qualification inputs are shell data (workflow-owner evidence)

Bind dispatch endpoint/model through step environment variables and quoted argv rather than interpolating them into shell source. Set contents:read and disable persisted checkout credentials; the API key remains step-local.

Offline PyYAML 6.0.1/exact-shell replay with a cargo stub (no build or endpoint): ordinary argv before/after passed. Command-substitution endpoint and model inputs executed the marker before; after they remained literal argv and no marker ran. Quote, semicolon, apostrophe, newline and glob inputs remained literal. A negative mutant reintroducing model interpolation was detected. No workflow dispatch or self-hosted runner execution was performed.

@aien-spark
aien-spark marked this pull request as ready for review October 26, 2026 15:10
@aien-spark
aien-spark merged commit 1de1b00 into main Oct 26, 2026
6 checks passed
@aien-spark
aien-spark deleted the fix/security-workspace-and-session-bounds branch October 26, 2026 15:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant