Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
86 commits
Select commit Hold shift + click to select a range
5c5dd82
security: harden audited storage, path and approval boundaries
Broccolito Oct 24, 2026
f3b162a
docs: specify recoverable folder access and handle-based file operations
Broccolito Oct 24, 2026
3f46589
Merge remote-tracking branch 'origin/main' into codex/security-review…
Broccolito Oct 24, 2026
4beeef9
security: enforce private session storage across platforms without st…
Broccolito Oct 24, 2026
a74f725
security: anchor host file operations to retained directory capabilities
Broccolito Oct 24, 2026
09154b4
security: scope approved external path aliases to each dispatch
Broccolito Oct 24, 2026
7d83897
security: protect undo storage with owner-private handle operations
Broccolito Oct 24, 2026
df2061b
security: refuse hard-linked private storage metadata changes
Broccolito Oct 24, 2026
e11cfd8
security: stage macOS replacement data behind a private directory ACL
Broccolito Oct 24, 2026
3f18d5f
test: verify Windows owner-private history APIs and ACLs
Broccolito Oct 24, 2026
03e9e54
fix: retain read-capable Windows private directory handles
Broccolito Oct 24, 2026
2250215
fix: preserve normal new-file ACL inheritance on macOS
Broccolito Oct 24, 2026
2a3504a
Anchor MCP editor and file executor I/O to directory capabilities
Broccolito Oct 24, 2026
e0507f6
Use brokered file authority and protect persistent undo storage
Broccolito Oct 24, 2026
dfb76c3
Migrate private undo history with bound identities and recoverable le…
Broccolito Oct 24, 2026
b9c4ebd
test: cover normal versus private macOS ACL inheritance
Broccolito Oct 24, 2026
a50eab1
Add protected external folder approval and resumable file operations
Broccolito Oct 24, 2026
96334e9
Verify nested folder approvals and document scoped browser access
Broccolito Oct 24, 2026
fef461c
test: compare approved alias display with its canonical path
Broccolito Oct 24, 2026
09d4bac
Bind structured analysis and image reads and precompute edit diagnostics
Broccolito Oct 24, 2026
c099540
Keep bounded post-edit diagnostics behind the trusted opt-in flag
Broccolito Oct 24, 2026
3fe23c7
Disclose scoped directory analysis and manage folder grants from loca…
Broccolito Oct 24, 2026
9088cb0
security: retain created parent identity across file writes and undo …
Broccolito Oct 24, 2026
82b6eb5
Preserve image format decoding and avoid redundant history rewrites
Broccolito Oct 24, 2026
ded4f85
Keep analysis caching alias-safe and clarify bounded policy failures
Broccolito Oct 24, 2026
6cdd369
Seal bounded edit diagnostics at trusted builtin dispatch
Broccolito Oct 24, 2026
25252dc
security: consume bounded post-edit diagnostics through one-shot rece…
Broccolito Oct 24, 2026
0ac9381
security: reject aliased session database files before opening SQLite
Broccolito Oct 24, 2026
c2282fd
Pin implicit installed-skill authority before file admission
Broccolito Oct 24, 2026
36987f0
test: remove unused editor compatibility wrappers
Broccolito Oct 24, 2026
ea64f14
security: apply stored directory denials to complete file plans
Broccolito Oct 24, 2026
f630b20
Retain workspace authority before the first file dispatch
Broccolito Oct 24, 2026
dd607a4
Reject folder identity changes before presenting approval
Broccolito Oct 24, 2026
c98486d
Normalize discovered file namespaces before granting access
Broccolito Oct 24, 2026
20bacc8
Capture trusted initial workspace aliases without widening file autho…
Broccolito Oct 24, 2026
3c0c5a8
Keep folder-scoped approval within existing session reach
Broccolito Oct 24, 2026
1639a1c
Keep legacy pathname guard fixtures out of production builds
Broccolito Oct 24, 2026
9c320a5
build: link bounded filesystem authority into the core crate
Broccolito Oct 24, 2026
ae219c2
docs: track integrated security verification and the Versa merge hold
Broccolito Oct 24, 2026
7ea36fe
api: generate folder access approval and grant schemas
Broccolito Oct 24, 2026
9b02350
fix: preserve file paths when discovering existing approval targets
Broccolito Oct 24, 2026
9c7097e
Merge validated long-horizon and operation reporting changes into sec…
Broccolito Oct 24, 2026
1dcadde
Accept the current Windows token default owner for private storage
Broccolito Oct 24, 2026
98d5802
Refuse unsafe Windows private-storage ACL migration
Broccolito Oct 24, 2026
4b11afa
test: verify rebuilt Linux session history across separate users
Broccolito Oct 24, 2026
9a28b63
Show safe recovery guidance for refused history startup
Broccolito Oct 24, 2026
d36d9dc
test: handle status-only session rename responses in storage probe
Broccolito Oct 24, 2026
b1fa88e
test: preserve bounded daemon shutdown and cleanup outcomes
Broccolito Oct 24, 2026
672655a
Give folder approval keys stable accessible labels
Broccolito Oct 24, 2026
a785acd
Record integration checkpoints and outstanding platform acceptance
Broccolito Oct 24, 2026
ad89882
Run bounded filesystem integration tests on Windows CI
Broccolito Oct 24, 2026
d347ccf
Align bounded file consumers with Rust lint conventions
Broccolito Oct 24, 2026
840fa5a
Simplify scoped folder decision validation
Broccolito Oct 24, 2026
11a61ac
Separate bounded editor command execution from admission
Broccolito Oct 24, 2026
dbd9f9b
Split folder approval policy into focused helpers
Broccolito Oct 24, 2026
39a947f
Refactor tool admission and result integration helpers
Broccolito Oct 24, 2026
22e9f84
test: verify persisted Developer undo across separate users
Broccolito Oct 24, 2026
3a027d6
Gate Unix-mode Docker assessment fixtures by platform
Broccolito Oct 24, 2026
91ffe91
Keep diagnostic receipt tests after production privacy ratchets
Broccolito Oct 24, 2026
4325b69
test: query readable legacy history from an observer-owned copy
Broccolito Oct 24, 2026
0c9d38b
Request scoped folder access before recursive analysis
Broccolito Oct 24, 2026
3f22656
Fix folder analysis authorization test request metadata
Broccolito Oct 24, 2026
0d49ef9
Discover Windows paths after their complete volume root
Broccolito Oct 24, 2026
668c6e1
Preserve editor newlines and correct Windows capability fixtures
Broccolito Oct 24, 2026
bc02d96
fix(ui): use shared shell for folder access controls
Broccolito Oct 24, 2026
3e1973f
docs(security): index assessment and integration records
Broccolito Oct 24, 2026
7a1c656
Record initial integration failures and corrective acceptance gates
Broccolito Oct 24, 2026
d336840
Lease Windows directory operations from retained identity handles
Broccolito Oct 24, 2026
a24a372
Verify idle Windows workspace moves retain file authority
Broccolito Oct 24, 2026
aca71f4
Recognize the synthetic analysis approval integration fixture
Broccolito Oct 24, 2026
9b029d8
fix: reopen Windows directory leases through retained native handles
Broccolito Oct 24, 2026
1307e41
Keep structured CLI output free of the startup banner
Broccolito Oct 24, 2026
c1ddb0e
Use unique completion identities in CLI output regression
Broccolito Oct 24, 2026
25f0a44
Give sandbox security integration tests a unique target name
Broccolito Oct 24, 2026
589a1b5
test(crew): construct refused runtime socket fixtures
Broccolito Oct 24, 2026
971ebed
Merge commit '48c154d4da59c3e9ec53583c94cacb92a046207a' into codex/se…
Broccolito Oct 24, 2026
d0063ed
Bind desktop folder grants to native client lifetime
Broccolito Oct 24, 2026
440e763
Reuse verified Unix socket for native consent lifetime handshake
Broccolito Oct 24, 2026
fe89041
fix(cli): keep workflow parameter summaries on stderr
Broccolito Oct 24, 2026
45619c9
Clarify folder grant lifetimes and validate native session ownership
Broccolito Oct 24, 2026
7ab8d1b
Register folder reach guards in the privacy census
Broccolito Oct 24, 2026
f9d5533
test: align native lifetime and Windows directory controls
Broccolito Oct 24, 2026
448abd5
test: preserve scoped native task handles without yielding futures
Broccolito Oct 24, 2026
f9e8ab6
refactor: keep direct tool execution in one scoped helper
Broccolito Oct 24, 2026
f227d13
test: use supported array indexing in native lifetime fixtures
Broccolito Oct 24, 2026
14b68c9
test: await published Crew access state after offline lookup
Broccolito Oct 24, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
16 changes: 12 additions & 4 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,14 @@ jobs:
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- name: Probe Windows directory reopen and nested-handle move semantics
if: runner.os == 'Windows'
timeout-minutes: 1
shell: pwsh
run: |
python scripts/security/windows-directory-lease-probe.py
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
python scripts/security/windows-nested-handle-probe.py
- uses: dtolnay/rust-toolchain@master
with:
toolchain: "1.92"
Expand Down Expand Up @@ -410,11 +418,11 @@ jobs:
# The cross-platform-sensitive suites BR-70 exists to prove, run on every
# OS they apply to. The workspace step above already runs the sandbox,
# developer shell/background and security unit tests through their library
# targets. The sandbox's integration target is the one `tests/` binary
# that runs on every OS (the rest follow in the next step, ubuntu only);
# name it directly rather than executing those unit tests a second time.
# targets. The sandbox integration runs on every OS; Windows also runs
# bounded_fs to exercise its retained-handle and ACL fixtures natively.
# Ubuntu runs bounded_fs in the offline integration step below.
- name: cargo test (sandbox integration)
run: cargo test -p biorouter-sandbox --test sandbox --locked --no-fail-fast
run: cargo test -p biorouter-sandbox --test sandbox ${{ matrix.os == 'windows-latest' && '--test bounded_fs' || '' }} --locked --no-fail-fast

# ── Every other `tests/*.rs` binary, bar a table of exceptions ─────────
#
Expand Down
134 changes: 134 additions & 0 deletions .github/workflows/security-storage.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
name: Rebuilt session storage acceptance

on:
workflow_dispatch:
pull_request:
paths:
- '.github/workflows/security-storage.yml'
- 'Cargo.toml'
- 'Cargo.lock'
- 'rust-toolchain.toml'
- 'crates/biorouter/src/session/**'
- 'crates/biorouter/src/config/paths.rs'
- 'crates/biorouter-sandbox/**'
- 'crates/biorouter-mcp/src/developer/**'
- 'crates/biorouter-mcp/src/lib.rs'
- 'crates/biorouter-server/**'
- 'scripts/security-assessment/session_storage_rebuilt.py'
- 'scripts/security-assessment/test_session_storage_rebuilt.py'
- 'scripts/security-assessment/session-storage-runtime.Dockerfile'
push:
branches: [main]
paths:
- '.github/workflows/security-storage.yml'
- 'Cargo.toml'
- 'Cargo.lock'
- 'rust-toolchain.toml'
- 'crates/biorouter/src/session/**'
- 'crates/biorouter/src/config/paths.rs'
- 'crates/biorouter-sandbox/**'
- 'crates/biorouter-mcp/src/developer/**'
- 'crates/biorouter-mcp/src/lib.rs'
- 'crates/biorouter-server/**'
- 'scripts/security-assessment/session_storage_rebuilt.py'
- 'scripts/security-assessment/test_session_storage_rebuilt.py'
- 'scripts/security-assessment/session-storage-runtime.Dockerfile'

permissions:
contents: read

concurrency:
group: security-storage-${{ github.event_name }}-${{ github.event_name == 'pull_request' && github.ref || github.run_id }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

jobs:
rebuilt-linux-sqlite:
runs-on: ubuntu-24.04
timeout-minutes: 60
env:
CARGO_BUILD_JOBS: 2
CARGO_PROFILE_DEV_DEBUG: 0
CARGO_TERM_COLOR: always
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- name: Record exact source before building
env:
SOURCE_SHA: ${{ github.sha }}
PR_HEAD_SHA: ${{ github.event.pull_request.head.sha }}
run: |
set -euo pipefail
export SECURITY_STORAGE_EVIDENCE="$RUNNER_TEMP/security-storage-evidence"
echo "SECURITY_STORAGE_EVIDENCE=$SECURITY_STORAGE_EVIDENCE" >> "$GITHUB_ENV"
mkdir -p "$SECURITY_STORAGE_EVIDENCE"
python3 - <<'PY'
import json, os, subprocess
from pathlib import Path
actual = subprocess.check_output(['git', 'rev-parse', 'HEAD'], text=True).strip()
assert actual == os.environ['SOURCE_SHA']
proof = {'source_sha': actual, 'pull_request_head_sha': os.environ['PR_HEAD_SHA'] or None,
'runner_os': 'ubuntu-24.04', 'build_profile': 'dev', 'status': 'source_recorded'}
(Path(os.environ['SECURITY_STORAGE_EVIDENCE']) / 'source.json').write_text(json.dumps(proof, indent=2) + '\n')
PY
- name: Check fixture protocol helpers without Docker or a database
run: python3 -m unittest discover -s scripts/security-assessment -p test_session_storage_rebuilt.py -v
- uses: dtolnay/rust-toolchain@master
with:
toolchain: '1.92'
- name: Keep the cache keyed to the pinned toolchain
run: |
set -euo pipefail
pinned=$(rustup show active-toolchain | cut -d' ' -f1)
test -n "$pinned"
for toolchain in $(rustup toolchain list | cut -d' ' -f1); do
if [ "$toolchain" != "$pinned" ]; then rustup toolchain uninstall "$toolchain"; fi
done
- uses: Swatinem/rust-cache@v2
with:
key: serve
save-if: false
- uses: arduino/setup-protoc@v3
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
- name: Install normal Linux build dependencies
run: |
set -euo pipefail
sudo rm -f /etc/apt/sources.list.d/microsoft-prod.list \
/etc/apt/sources.list.d/azure-cli.list /etc/apt/sources.list.d/microsoft-prod.sources \
/etc/apt/sources.list.d/azure-cli.sources
sudo apt-get update
sudo apt-get install --yes --no-install-recommends libdbus-1-dev libxcb1-dev libxdo-dev pkg-config
- name: Rebuild the actual daemon from the checked-out source
run: |
set -euo pipefail
cargo build --locked -p biorouter-server --bin biorouterd 2>&1 | tee "$SECURITY_STORAGE_EVIDENCE/build.log"
git diff --exit-code HEAD
rustc -vV > "$SECURITY_STORAGE_EVIDENCE/rustc.txt"
sha256sum target/debug/biorouterd > "$SECURITY_STORAGE_EVIDENCE/daemon.sha256"
- name: Build a matching Ubuntu runtime
run: |
set -euo pipefail
docker build --pull --tag biorouter-security-storage-runtime:ci \
--file scripts/security-assessment/session-storage-runtime.Dockerfile \
scripts/security-assessment 2>&1 | tee "$SECURITY_STORAGE_EVIDENCE/runtime-build.log"
docker image inspect biorouter-security-storage-runtime:ci > "$SECURITY_STORAGE_EVIDENCE/runtime-image.json"
- name: Exercise real SQLite and Developer undo with two separate non-root accounts
env:
SOURCE_SHA: ${{ github.sha }}
run: |
set -euo pipefail
python3 scripts/security-assessment/session_storage_rebuilt.py \
--daemon target/debug/biorouterd \
--image biorouter-security-storage-runtime:ci \
--source-commit "$SOURCE_SHA" \
--repetitions 3 \
--output "$SECURITY_STORAGE_EVIDENCE/rebuilt-runtime" \
2>&1 | tee "$SECURITY_STORAGE_EVIDENCE/acceptance.log"
- uses: actions/upload-artifact@v4
if: always()
with:
name: rebuilt-session-storage-${{ github.sha }}
path: ${{ runner.temp }}/security-storage-evidence/
if-no-files-found: error
retention-days: 30
20 changes: 20 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

47 changes: 46 additions & 1 deletion biorouter-self-test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ activities:
- Exercise isolated OKF, BioOKF, and built-in Soul migration round trips
- Generate and execute test workflows
- Test error boundaries and security controls
- Verify protected external-folder approval, in-place result reads, cancellation, and revocation using synthetic files
- Verify asynchronous operation failures remain visible without turning successful status lookups into tool errors
- Validate the Settings reset contract and its active-work safety gate
- Verify provider error handling, conversation-scoped reasoning, and saved-credential deletion safeguards
Expand All @@ -39,7 +40,7 @@ parameters:
input_type: string
requirement: optional
default: "all"
description: "Which test phases to run: all, basic, extensions, computer_use, crew, apps, previews, subagents, knowledge, workflows, reset, providers, local_inference, agent_loop, operation_outcome, office, advanced"
description: "Which test phases to run: all, basic, extensions, computer_use, crew, apps, previews, subagents, knowledge, workflows, reset, providers, local_inference, agent_loop, operation_outcome, office, advanced, folder_access"

- key: test_depth
input_type: string
Expand Down Expand Up @@ -251,6 +252,50 @@ instructions: |

{% endif %}

{% if test_depth != "fixture_only" and (test_phases == "all" or "folder_access" in test_phases) %}
## PHASE 4A-F: External-folder authorization

Use only disposable synthetic files and a fresh isolated profile; never authorize a real home,
credential, or patient-data directory. Do not use shell or computer tools to bypass a denial.
1. Run `cargo test -p biorouter --test folder_access_authorization` for exact-file once scope,
session grants, read-to-write escalation, denial, cancellation, wrong-session decisions,
unproven decisions, connector-result reads in place, revocation, and unattended recovery.
2. In an interactive desktop chat, request Developer view on a synthetic completed connector
result outside its workspace. Confirm the displayed folder, read-only access, file list,
and session lifetime. Choose Allow once and verify the original result is read in place.
3. Request an external synthetic write; choose Deny and verify no file changed and no repeated
request is shown during the same turn. Retry on a fresh user turn and cancel the request.
4. Grant a specific synthetic folder for this session, create and undo a file there, then open
Folder access beneath the composer and revoke it. The next external write must ask again.
Repeat grant inspection and revocation in the local CLI using `/folder-access` and
`/folder-access revoke <id>`. For directory analysis, verify the once-only read of descendants
is disclosed and grants no writes or remembered session access.
5. Record browser proof limitations explicitly if the tested browser surface cannot approve.
A disabled control, screenshot, or synthetic provider fixture is not a live model-driven pass.
6. With closed standard input and no `--quiet` workaround, request an unapproved external read
using `--output-format json`. Parse the entire stdout as one JSON object; verify the actual
tool result contains `authorization_required`, the requested scope, and interactive recovery
guidance without file content. Repeat with `stream-json` and parse every output line as JSON.
Repeat with a parameterized workflow; its human parameter summary must remain on stderr.
Run `cargo test -p biorouter-cli --test non_interactive run_output_formats_keep_machine_stdout_parseable`
for the real-binary format controls; ordinary text output must retain the session banner.

7. Manual headed-desktop lifecycle gate (#455): in an isolated managed profile, approve a
synthetic external folder for this chat, quit the actual app normally, reopen the same chat,
and request that file again. Require a new folder card before any read. Also exercise abrupt
native disconnection, reconnect, and two active clients using the same chat: ending one client
must revoke its retained once/session access and pending request without stopping the shared
daemon or revoking the other client's grants. Do not treat this CLI workflow, a unit test,
or a screenshot as proof that OS app quit/restart passed; record unrun actions as NOT EXERCISED.
8. Confirm lifetime notices match deployment: managed desktop says app quit/restart; browser
and manual external backends say backend restart and explicitly retain grants across interface
restart. Include the environment-selected external backend. Nonshared/native Windows retains
its owned-daemon normal-quit cleanup; do not claim it uses the Unix shared-client protocol.

Log exact commands, model/provider, human decisions, and observed results to:
{{ workspace_dir }}/phase4_folder_access.md
{% endif %}

{% if test_depth != "fixture_only" and (test_phases == "all" or "advanced" in test_phases) %}
### Phase 4: Advanced Self-Testing
Push boundaries and test limits:
Expand Down
21 changes: 21 additions & 0 deletions crates/biorouter-cli/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1493,6 +1493,13 @@ struct ServeOptions {
)]
computer_use_approval: bool,

/// Configure a separate key for external-folder decisions in the browser
#[arg(
long,
help = "Interactively set a folder-access approval key; requires a terminal"
)]
folder_access_approval: bool,

/// Open a browser once it is ready
#[arg(long, help = "Open a browser once the server is ready")]
open: bool,
Expand All @@ -1509,6 +1516,7 @@ impl ServeOptions {
self.public_url,
self.open,
self.computer_use_approval,
self.folder_access_approval,
)
.await
}
Expand Down Expand Up @@ -3742,8 +3750,21 @@ mod cli_tests {
assert!(options.web_dir.is_none());
assert!(!options.no_token);
assert!(!options.computer_use_approval);
assert!(!options.folder_access_approval);
assert!(!options.open);

let parsed = Cli::try_parse_from([
"biorouter",
"serve",
"--folder-access-approval",
"--computer-use-approval",
])
.unwrap();
let Some(Command::Serve(options)) = parsed.command else {
panic!("serve command expected")
};
assert!(options.folder_access_approval && options.computer_use_approval);

let parsed = Cli::try_parse_from([
"biorouter",
"headless",
Expand Down
Loading
Loading