diff --git a/.codex/config.toml b/.codex/config.toml index 66836ca..45ac5cf 100644 --- a/.codex/config.toml +++ b/.codex/config.toml @@ -5,13 +5,14 @@ approval_policy = "on-request" # work inside the sandbox without prompts; ask to cross the boundary sandbox_mode = "workspace-write" # read repo, edit workspace, run local commands -web_search = "disabled" # no external integrations (D5) +web_search = "disabled" # no external integrations [features] hooks = true [sandbox_workspace_write] -# Network OFF by design (v1). This blocks `git push` (good) AND `git fetch` — so there is NO +# Network OFF by design. This blocks `git push` (good) AND `git fetch` — so there is NO # in-session fetch on Codex; refresh remote state OUTSIDE the session first (see docs/agent-config.md). -# Intentional deviation from scope D4's "fetch allowed" line — pending human sign-off. +# Keeping fetch outside the session is the deliberate trade-off: enabling network would remove the +# sandbox layer from the push denial. network_access = false diff --git a/.codex/rules/agent-lab.rules b/.codex/rules/agent-lab.rules index 9315ef5..c8c2fc0 100644 --- a/.codex/rules/agent-lab.rules +++ b/.codex/rules/agent-lab.rules @@ -2,13 +2,13 @@ # Codex execpolicy: allow local git + forbid remote git/PR. The PreToolUse guard # (tools/pretooluse-guard.sh) is the AUTHORITATIVE denier; these are belt-and-suspenders. # Token-based: path-prefixes and origin/* refs are guard-enforced, not expressible here. -# CODEX_V1: 'git fetch' intentionally omitted — workspace-write sandbox is network-off. +# 'git fetch' intentionally omitted — workspace-write sandbox is network-off. # -- allow: local git (clean token prefixes only; workspace-write auto-runs the rest) -- prefix_rule(pattern = ["shellcheck"], decision = "allow", justification = "agent-lab lint") prefix_rule(pattern = ["git", "add"], decision = "allow", justification = "agent-lab local-git set") prefix_rule(pattern = ["git", "commit"], decision = "allow", justification = "agent-lab local-git set") -# CODEX_V1: ["git","fetch"] N/A — network-off +# ["git","fetch"] N/A — network-off prefix_rule(pattern = ["git", "switch"], decision = "allow", justification = "agent-lab local-git set") prefix_rule(pattern = ["git", "checkout"], decision = "allow", justification = "agent-lab local-git set") prefix_rule(pattern = ["git", "branch"], decision = "allow", justification = "agent-lab local-git set") diff --git a/.devguard/README.md b/.devguard/README.md index 37f5237..369821e 100644 --- a/.devguard/README.md +++ b/.devguard/README.md @@ -10,7 +10,7 @@ Files: Example: ```bash -./scripts/dev/guard-diff M2 +./scripts/dev/guard-diff release ``` -Then `.devguard/forbid-M2.txt` is applied in addition to defaults. +Then `.devguard/forbid-release.txt` is applied in addition to defaults. diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md new file mode 100644 index 0000000..4577f61 --- /dev/null +++ b/.github/CONTRIBUTING.md @@ -0,0 +1,26 @@ +# Contributing to agent-lab + +**This project does not accept contributions from non-members.** + +Pull requests and issues from anyone who is not a member of the organization will be closed without review. + +## Why? + +`agent-lab` is a specialized, opinionated containment lab. It is developed under a strict internal process (see `AGENTS.md`, `doctrine/`, and the guard tooling) to maintain its security and design invariants. We do not have the bandwidth or model to review external contributions. + +## What you _can_ do + +- Use the code locally for your own experiments (Apache 2.0 license). +- Fork the repository for personal or internal use. +- File issues **only** if you are a member (they will still be triaged internally). +- Report security issues privately (see [SECURITY.md](../SECURITY.md)). + +## For organization members + +- Follow the documented internal workflow. +- Never push or open PRs from outside the defined process. +- Work is done on agent branches; the human owns the remote gate. + +If you have questions about usage of the lab itself (running your own copy), the README and docs inside the repo are the authoritative source. There is no separate public support channel. + +Thank you for understanding. diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..d5186cd --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,30 @@ +> **Note to external users:** This project does **not** accept contributions or bug reports from non-members. Issues opened by non-members will be closed. Please do not open this issue if you are not a member. + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Run '....' +3. See error + +**Expected behavior** +A clear description of what you expected to happen. + +**Environment (if relevant)** +- OS: [e.g. Ubuntu 24.04] +- Docker version: +- Compose version: +- Branch / commit: + +**Additional context** +Add any other context about the problem here. + +**Screenshots or logs** (if applicable) + +--- + +_Only members should submit this. External submissions will be closed._ diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..eac6a4c --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,11 @@ +blank_issues_enabled: false +contact_links: + - name: 🔒 Security Vulnerability Report + url: https://github.com/uscient/agent-lab/blob/master/SECURITY.md + about: Please report security issues privately via the channel described in SECURITY.md. Do not open a public issue. + - name: ❓ Usage questions or help running the lab + url: https://github.com/uscient/agent-lab#readme + about: This repo is published for reference. Refer to the README, docs/, and code comments. We do not provide public user support or triage. + - name: 📖 Project documentation + url: https://github.com/uscient/agent-lab + about: Start with the README. Most answers are already in the repo. \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/general_feedback.md b/.github/ISSUE_TEMPLATE/general_feedback.md new file mode 100644 index 0000000..adbdad4 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/general_feedback.md @@ -0,0 +1,9 @@ +> **Note:** This project does not accept external contributions, feature requests, or general feedback via GitHub issues from non-members. This issue will be closed. + +This repository is a reference implementation of a containment lab. + +- For usage of the lab: read the README and files in the repo. +- For security: see SECURITY.md and use the private reporting channel. +- For everything else: this is not a community project accepting input from outside the organization. + +If you are a member and have legitimate internal feedback, please use the proper internal channels instead of (or in addition to) opening an issue. diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..77f5e19 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,34 @@ +> **⚠️ Important policy notice** +> +> This repository **does not accept contributions from non-members of the organization**. +> +> Any pull request opened by a non-member will be closed without review or merge. +> +> If you are an authorized member, please delete the notice above and continue. + +--- + +## Summary + + + +## Motivation / Context + + + +## Changes + + + +## Testing + + + +## Checklist (members only) + +- [ ] Change is minimal and focused +- [ ] Tests / validation scripts pass (`./scripts/dev/check ...`) +- [ ] No secrets or policy violations introduced +- [ ] Follows internal process (AGENTS.md / doctrine) + +If you are not a member, please close this PR instead of submitting. diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..05fdfa5 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,42 @@ +name: CI + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +on: + push: + branches: [master, main] + pull_request: + branches: [master, main] + +permissions: + contents: read + +jobs: + ci: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: docker/setup-buildx-action@v3 + + - name: Install shellcheck + run: sudo apt-get update -qq && sudo apt-get install -y -qq shellcheck + + - name: Lint + run: ./scripts/dev/lint-scripts + + - name: Check + validate + run: | + ./scripts/dev/check default full + ./tools/validate.sh + + - name: Build devbox + run: | + docker buildx build \ + --cache-from type=gha \ + --cache-to type=gha,mode=max \ + --load \ + -t agent-lab/devbox:local \ + -f images/devbox/Dockerfile . diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 0000000..a335347 --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,30 @@ +name: CodeQL + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +on: + push: + branches: [master, main] + pull_request: + branches: [master, main] + schedule: + - cron: "0 6 * * 1" + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + steps: + - uses: actions/checkout@v4 + + - uses: github/codeql-action/init@v3 + with: + languages: actions + + - uses: github/codeql-action/analyze@v3 diff --git a/.gitignore b/.gitignore index 5e77bfe..4774df2 100644 --- a/.gitignore +++ b/.gitignore @@ -51,3 +51,7 @@ node_modules/ # Docker compose override files may contain local paths or secrets compose.override.yaml compose.*.override.yaml + +# Temp location for machine account token (used for xor-machine auth while commits appear as xormania) +# Will be deleted after setup. Never commit tokens. +/tmp/ignore/ diff --git a/LICENSE b/LICENSE index e52d8af..f3f6440 100644 --- a/LICENSE +++ b/LICENSE @@ -1,21 +1,200 @@ -MIT License - -Copyright (c) 2026 The agent-lab Authors - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2026 The agent-lab Authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/OVERLAY-README.md b/OVERLAY-README.md deleted file mode 100644 index 642d501..0000000 --- a/OVERLAY-README.md +++ /dev/null @@ -1,30 +0,0 @@ -# agent-lab — mutation-policy overlay - -Unzip these files **into your existing `agent-lab/` project root.** They ADD the mutation-policy enforcement and helper tools that agent-lab's committed config didn't include — and they overwrite nothing (every filename is new). Run `unzip -l` first to confirm. - -## Source of truth is unchanged -**`agent-lab/AGENTS.md` remains authoritative** for the security/containment doctrine (network, filesystem, privilege, secret, and supply-chain invariants; threat model; hard stops; ask-before list). This overlay does not touch it. It only layers the same *mutation policy* used at the workspace top level, expressed in each tool's enforcement primitives. - -## What it adds -| Path | Purpose | -|------|---------| -| `.claude/settings.json` | Committed enforcement: `defaultMode: plan`, deny git mutation/PR + secrets/control-plane, `ask` on edits, PreToolUse hook. Extends the existing `settings.local.example.json`. | -| `.claude/commands/{plan,recon,revise}.md` | `tmp/`-writing, security-aware slash commands (alongside agent-lab's existing review commands). | -| `.claude/doctrine-overlay.md` | How the mutation policy maps onto Claude Code. | -| `.codex/config.toml` | Read-only sandbox by default; opt-in `--profile edit`. (agent-lab had only `.codex/prompts/`.) | -| `.codex/doctrine-overlay.md` | Codex mapping. | -| `.grok/{config.toml,settings.json,doctrine-overlay.md}` | Grok Build / grok-cli config (agent-lab had none). | -| `tools/pretooluse-guard.sh` | Hard stop: git mutation, `gh pr`, `sed -i`, `rm -rf`, sudo, control-plane writes — **plus** docker.sock / `--privileged` / host-net / secret writes. | -| `tools/containment-lint.sh` | Content scan for boundary breaks + real secrets (complements `scripts/dev/guard-diff`). | -| `tools/validate.sh` | `docker compose config` + containment-lint. Never `up`. | -| `tools/new-revision.sh` | New lineage-named `tmp/` revision (never overwrite). | -| `tmp/.gitkeep` | Agent working docs land here as revisions. | - -## Same mutation policies as the top level -- No commit / push / PR — git authority is the human's (suggest a commit message only). -- No edits unless told — plan mode is the default; editing is opt-in per task. -- All agent docs → `tmp/` as NEW revisions. -- Smallest useful patch; strict, docker-aware bash; read selectively / cite `path:line`. - -## Note -This overlay's `settings.json` is intentionally stricter than agent-lab's "allowed work without approval" list (the stricter rule wins): it tightens those into "plan + explicit edit, never commit," while `AGENTS.md` continues to carry the real security boundary. Working docs land in `tmp/` (now gitignored by default). Use external handoff (Drive etc.) when sharing revisions. diff --git a/README.md b/README.md index 0b492d5..65e8e6c 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,15 @@ # agent-lab -`agent-lab` is a private Docker Compose containment lab for experimenting with autonomous agent workloads behind explicit network, filesystem, credential, and egress controls. +`agent-lab` is a Docker Compose containment lab for experimenting with autonomous agent workloads behind explicit network, filesystem, credential, and egress controls. -It is not a general AI application stack, a public SaaS control plane, or an unrestricted agent launcher. The v0 slice implements the containment substrate — an internal agent network, controlled DNS, a Squid egress proxy, and acceptance tests — plus a bring-your-own-agent profile (`scripts/agent`) that runs any agent image on your project behind those controls. +> **Repository status (public mirror)** +> +> This code is published under the Apache 2.0 license for transparency, reference, and local use/forking. +> **No contributions are accepted from non-members.** Pull requests and issues from outside the organization will be closed without review. +> +> See [CONTRIBUTING.md](.github/CONTRIBUTING.md) and [SECURITY.md](SECURITY.md) for details. + +It is not a general AI application stack, a public SaaS control plane, or an unrestricted agent launcher. It implements the containment substrate — an internal agent network, controlled DNS, a Squid egress proxy, and acceptance tests — plus a bring-your-own-agent profile (`scripts/agent`) that runs any agent image on your project behind those controls. ## Default Posture @@ -17,7 +24,7 @@ It is not a general AI application stack, a public SaaS control plane, or an unr - Squid is the only sanctioned outbound path. - Proxy environment variables help cooperating tools, but they are not the security boundary. -This is practical Docker containment, not VM-grade isolation. Containers still share the host kernel. A container-runtime or kernel escape is outside the guarantees of this v0 lab. +This is practical Docker containment, not VM-grade isolation. Containers still share the host kernel. A container-runtime or kernel escape is outside the guarantees of this lab. ## Quick Start @@ -103,7 +110,7 @@ No-internet mode is `core` plus `devtools`, without `egress-proxy`. The test con Allowlisted-egress mode is `core` plus `egress` plus `devtools`. The test container still has no direct internet route. Cooperating tools can use `HTTP_PROXY` or `HTTPS_PROXY` to reach Squid at `172.30.0.20:3128`. Squid allows only domains in `policies/egress.allowlist.example` by default. -Raw direct egress attempts are blocked by the internal Docker network, but they are not logged in v0 unless optional host firewall hardening is added later. Proxy-mediated allowed and denied requests are logged by Squid. +Raw direct egress attempts are blocked by the internal Docker network, but they are not logged unless optional host firewall hardening is added. Proxy-mediated allowed and denied requests are logged by Squid. ## Static Network Defaults @@ -115,9 +122,28 @@ Squid: 172.30.0.20:3128 These values are duplicated in `.env.example`, `compose.yaml`, `compose.egress.yaml`, `dns/coredns/Corefile`, and `gateway/squid/squid.conf` where needed so the generated configuration stays readable. -## What v0 Does Not Prove +## What This Lab Does Not Prove -- TLS SNI peek/splice enforcement is not enabled yet. Squid currently enforces the CONNECT host/domain allowlist, private destination denies, unsafe port denies, and default deny. SNI mismatch protection is an explicit M3 TODO. +- TLS SNI peek/splice enforcement is not enabled yet. Squid currently enforces the CONNECT host/domain allowlist, private destination denies, unsafe port denies, and default deny. SNI mismatch protection is not yet implemented. - Raw blocked attempts are not logged without a future host firewall layer. - IPv6 is not enabled on the `agents` network. If Docker IPv6 is enabled host-wide, re-audit before relying on these rules. - Allowlisted domains can still receive exfiltrated data. The allowlist bounds where data may go, not what data is sent. + +## License + +Licensed under the [Apache License 2.0](LICENSE). + +## Contributing & PR Policy + +**No contributions are accepted from non-members.** + +- External pull requests will be closed. +- External issues will generally be closed (see the issue templates for redirects to documentation and private reporting). + +Organization members: see `AGENTS.md`, `doctrine/`, and the internal process. + +For everyone else: the repository is provided as-is for you to review or run locally. We are not accepting changes, feature requests, or support requests from the public. + +## Security + +See [SECURITY.md](SECURITY.md) for the lab's security model and how to report vulnerabilities (private channel only). diff --git a/SECURITY.md b/SECURITY.md index 635e29c..a389fa1 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -30,7 +30,7 @@ Stop work and report before continuing if tracked source contains or introduces: Agent and test containers must attach only to the internal `agents` network. Only `egress-proxy` may attach to both `agents` and `egress`. -No service publishes public ports in v0. Future operator-facing ports must bind to `127.0.0.1` unless explicitly approved. +No service publishes public ports. Any operator-facing ports must bind to `127.0.0.1` unless explicitly approved. The Docker socket must never be mounted into an agent container. Host home directories, SSH directories, cloud-drive roots, browser profiles, and password-manager data must never be mounted into agent containers. @@ -40,4 +40,8 @@ Domain allowlisting controls where traffic can go. It does not control what an a ## Vulnerability Reporting -This is a private lab. Report suspected boundary bypasses, secret exposure, or dangerous defaults to the repository owner through the private project channel used for this repository. +**This is a closed project.** Do not open public GitHub issues for security matters. + +Report suspected boundary bypasses, secret exposure, or dangerous defaults to the repository owner through the private project channel used for this repository. + +See also the contributing policy in the repository for details on external vs. internal participation. diff --git a/THREAT_MODEL.md b/THREAT_MODEL.md index 83a1b6f..b69c68a 100644 --- a/THREAT_MODEL.md +++ b/THREAT_MODEL.md @@ -32,7 +32,7 @@ These are trusted components, not perfect components. Bugs or misconfiguration i - Only `egress-proxy` is dual-homed to `agents` and `egress`. - CoreDNS is pinned as DNS for agent/test containers and refuses arbitrary external recursion. - Squid enforces a minimal domain allowlist, denies private/link-local/loopback/metadata ranges, denies unsafe ports, and defaults to deny. -- No Docker socket, host home mounts, privileged containers, host networking, or public ports are used in v0. +- No Docker socket, host home mounts, privileged containers, host networking, or public ports are used. ## Agent Profile @@ -44,15 +44,15 @@ Writable surfaces are explicit and fall into three classes: - Agent state / cache — `/home/agent`, a persistent `agent-home` named volume by default. This volume can become credential-bearing (OAuth/login tokens an agent writes after sign-in) and is the main reason to prefer `AGENT_LAB_EPHEMERAL_HOME=1`, which maps it to tmpfs so nothing persists. - Service / workspace data — `/workspace` (the project dir or a named volume) and `/tmp` (tmpfs). The rootfs itself stays read-only. -Egress remains deny-by-default: the `base` recipe is empty, so an agent with no recipe reaches nothing, including its own API. Recipes are additive allowlist fragments; Squid still denies private ranges, raw IPs, and unsafe ports ahead of the allow rule, and matches the CONNECT host, not the TLS SNI (an M3 TODO). +Egress remains deny-by-default: the `base` recipe is empty, so an agent with no recipe reaches nothing, including its own API. Recipes are additive allowlist fragments; Squid still denies private ranges, raw IPs, and unsafe ports ahead of the allow rule, and matches the CONNECT host, not the TLS SNI (not yet implemented). ## Residual Risks - This is practical Docker containment, not VM isolation. A host kernel or container-runtime escape defeats this design. - Allowlisted destinations can receive exfiltrated data. - Raw direct egress attempts are blocked by lack of route but are not logged unless a future host firewall layer is added. -- TLS SNI mismatch protection is not implemented in v0. Squid enforces CONNECT host/domain allowlisting only. -- If Docker IPv6 is enabled host-wide, IPv6 must be re-audited. The v0 `agents` network is intended to be IPv4 only. +- TLS SNI mismatch protection is not implemented. Squid enforces CONNECT host/domain allowlisting only. +- If Docker IPv6 is enabled host-wide, IPv6 must be re-audited. The `agents` network is intended to be IPv4 only. - DNS-over-HTTPS can reintroduce external DNS if a DoH endpoint is allowlisted. - Squid and CoreDNS are part of the trust base. - Upstream infrastructure images (CoreDNS, Squid) are pinned by non-`latest` tags, not digests; digest-pinning them remains a TODO. The OpenClaw image already digest-pins its bases, and `images/devbox/Dockerfile` carries a digest-pin TODO for `debian:bookworm-slim`. diff --git a/dns/coredns/README.md b/dns/coredns/README.md index 1bb7aa8..3986eff 100644 --- a/dns/coredns/README.md +++ b/dns/coredns/README.md @@ -2,7 +2,7 @@ CoreDNS is the resolver pinned into agent/test containers on the internal `agents` network. -The v0 Corefile: +The Corefile: - Listens on port 53 inside the `agents` network. - Logs queries to container stdout. @@ -13,4 +13,4 @@ The v0 Corefile: External name resolution for allowed outbound requests is done by Squid on the `egress` network. Agent/test containers should not receive arbitrary external A or AAAA records. -CoreDNS does not write query logs into the `audit` volume in v0 because the stock CoreDNS log plugin writes to stdout. Squid proxy logs are stored in `audit`. +CoreDNS does not write query logs into the `audit` volume because the stock CoreDNS log plugin writes to stdout. Squid proxy logs are stored in `audit`. diff --git a/docs/agent-config.md b/docs/agent-config.md index eeffcdb..1177f00 100644 --- a/docs/agent-config.md +++ b/docs/agent-config.md @@ -1,4 +1,4 @@ -# Agent configuration — setup & maintenance (D7) +# Agent configuration — setup & maintenance How the three coding agents that **develop Agent Lab** (Claude Code, Codex, Grok) are configured to work autonomously inside a hard boundary: commit locally, never push/PR, never weaken containment. @@ -71,13 +71,13 @@ native `deny`; protected-path read-only enforcement is left to the guard's `Edit | Tool | Setup | |---|---| -| **Claude Code** | No trust step. `.claude/settings.json` is auto-loaded (delete the legacy `.claude/settings.local.json`). | +| **Claude Code** | No trust step. `.claude/settings.json` is auto-loaded. | | **Codex** | Trust the project so `.codex/` (config, hooks, rules) loads — Codex ignores an untrusted project's `.codex/`. Verify with a guard-fired check (a deliberately-bad command must print the guard's BLOCKED message). | -| **Grok** | Trust project hooks, or `.grok/hooks/` are **silently skipped** (D4 would falsely pass). Use `grok inspect` to confirm hooks/config were discovered, then run the guard-fired check. | +| **Grok** | Trust project hooks, or `.grok/hooks/` are **silently skipped** (and a policy check would falsely pass). Use `grok inspect` to confirm hooks/config were discovered, then run the guard-fired check. | -### Codex network-off runbook (intentional D4 deviation — pending scope sign-off) +### Codex network-off runbook (intentional design decision) -Codex v1 runs `sandbox_mode = "workspace-write"` with `network_access = false`. That blocks push +Codex runs `sandbox_mode = "workspace-write"` with `network_access = false`. That blocks push (good) **and** `git fetch`. So **`git fetch` is not available inside a Codex session — by design.** Refresh remote state *outside* Codex **before** the session starts: @@ -86,9 +86,8 @@ git fetch origin # human / CI / wrapper, before launching codex codex … # works against the freshly-fetched local refs ``` -Do **not** add a network-on Codex profile to "fix" fetch in v1 — enabling network would remove the -sandbox layer from the push denial. (This deviates from scope D4's "fetch allowed" line and needs the -human's sign-off.) +Do **not** add a network-on Codex profile to "fix" fetch — enabling network would remove the +sandbox layer from the push denial. Keeping fetch outside the session is the deliberate trade-off. ## Forbidden flags (never use these as the autonomy mechanism) @@ -125,9 +124,3 @@ may be doing unrelated work; the dev git-policy does not apply inside them. `AGENT_LAB_PROJECT_DIR` at the repo root (RW) with a coding-agent image — caller configuration, not a baked runtime role. The repo-scoped configs travel with the repo, so an agent editing Agent Lab inherits the dev policy wherever it runs. - -## Superseded - -`OVERLAY-README.md` documents the **old** overlay posture (never-commit, plan-mode default) that this -overhaul inverts (commits are now allowed; autonomy is broad inside the boundary). Treat it as -**superseded** by this document; remove it once no workflow references it. diff --git a/doctrine/git-workflow.md b/doctrine/git-workflow.md index 8370504..d991e98 100644 --- a/doctrine/git-workflow.md +++ b/doctrine/git-workflow.md @@ -8,7 +8,7 @@ TL;DR: Work on agent//; commit freely; never push, pull, or integrat - **Denied (guard, exit 2):** push (any form); pull; `merge`/`rebase` from a remote-tracking ref (`origin/*`, `refs/remotes/*`) — that is the 2nd half of a pull, so denying `pull` alone is not enough; `gh pr` / remote-writing `gh`; `git remote` mutation. Enforced — don't route around them. -- **Profile note:** under Codex v1 the sandbox network is off, so `fetch` isn't available in-session; +- **Profile note:** under Codex the sandbox network is off, so `fetch` isn't available in-session; the human / CI / wrapper refreshes remote state before the session starts. - **Done** = a clean local commit on the branch + a short handoff. The human reviews and opens the PR. diff --git a/gateway/squid/README.md b/gateway/squid/README.md index 9876eef..021cd93 100644 --- a/gateway/squid/README.md +++ b/gateway/squid/README.md @@ -1,8 +1,8 @@ # Squid Egress Proxy -Squid is the only v0 service attached to both the internal `agents` network and the internet-capable `egress` network. +Squid is the only service attached to both the internal `agents` network and the internet-capable `egress` network. -The v0 policy: +The policy: - Listens on port `3128` inside Docker networks only. - Allows clients only from `172.30.0.0/24`. @@ -17,9 +17,9 @@ The v0 policy: ## TLS/SNI Status -TLS SNI peek/splice is not implemented in v0. Squid currently enforces the CONNECT hostname/domain allowlist before tunneling HTTPS, but it does not inspect the TLS ClientHello SNI. +TLS SNI peek/splice is not implemented. Squid currently enforces the CONNECT hostname/domain allowlist before tunneling HTTPS, but it does not inspect the TLS ClientHello SNI. -This means v0 has not proven protection against a crafted client that CONNECTs to an allowed hostname while sending a different SNI value inside TLS. That is an M3 TODO and must be validated before claiming SNI mismatch protection. +This means the lab has not proven protection against a crafted client that CONNECTs to an allowed hostname while sending a different SNI value inside TLS. That remains a TODO and must be validated before claiming SNI mismatch protection. ## Hardening Status diff --git a/images/openclaw/Dockerfile b/images/openclaw/Dockerfile index 0d0e633..f77cc80 100644 --- a/images/openclaw/Dockerfile +++ b/images/openclaw/Dockerfile @@ -1,6 +1,6 @@ # syntax=docker/dockerfile:1.7 -# M1 self-build scaffold for OpenClaw. +# Self-build scaffold for OpenClaw. # Build context must be the verified source directory produced by # scripts/openclaw-fetch-source. Do not clone moving branches in this Dockerfile. @@ -120,21 +120,21 @@ ARG OPENCLAW_BUNDLED_PLUGIN_DIR ARG OPENCLAW_NODE_BOOKWORM_SLIM_DIGEST LABEL org.opencontainers.image.title="agent-lab OpenClaw" \ - org.opencontainers.image.description="Self-built OpenClaw runtime scaffold for agent-lab locked v0 hardening" \ + org.opencontainers.image.description="Self-built OpenClaw runtime scaffold for agent-lab locked hardening" \ org.opencontainers.image.source="${OPENCLAW_SOURCE_REPO}" \ org.opencontainers.image.revision="${OPENCLAW_SOURCE_COMMIT}" \ org.opencontainers.image.base.name="docker.io/library/node:24-bookworm-slim" \ org.opencontainers.image.base.digest="${OPENCLAW_NODE_BOOKWORM_SLIM_DIGEST}" \ agent-lab.profile="openclaw" \ - agent-lab.capability-posture="locked-v0-build-scaffold" \ + agent-lab.capability-posture="locked-build-scaffold" \ agent-lab.openclaw.sandbox="disabled" \ agent-lab.openclaw.browser="not-installed" \ agent-lab.openclaw.docker-cli="not-installed" WORKDIR /app -# Keep the first runtime close to upstream. Package reduction is an M2+ task -# after locked-mode behavior is proven. This intentionally does not install +# Keep the first runtime close to upstream. Package reduction is deferred +# until locked-mode behavior is proven. This intentionally does not install # Docker CLI, Chromium, Playwright browser binaries, or OpenClaw sandbox support. RUN --mount=type=cache,id=agent-lab-openclaw-apt-cache,target=/var/cache/apt,sharing=locked \ --mount=type=cache,id=agent-lab-openclaw-apt-lists,target=/var/lib/apt,sharing=locked \ @@ -195,7 +195,7 @@ ENV HOME=/home/node \ USER node -# M2/M3 must verify this probe remains valid after locked config/preflight is added. +# Later work must verify this probe remains valid after locked config/preflight is added. HEALTHCHECK --interval=3m --timeout=10s --start-period=15s --retries=3 \ CMD node -e "fetch('http://127.0.0.1:18789/healthz').then((r)=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))" diff --git a/images/openclaw/README.md b/images/openclaw/README.md index dc1d3d7..8186f66 100644 --- a/images/openclaw/README.md +++ b/images/openclaw/README.md @@ -1,14 +1,14 @@ # OpenClaw Image Scaffold -This directory is the M1 source-pin and self-build scaffold for OpenClaw. +This directory is the source-pin and self-build scaffold for OpenClaw. ## Strategy `agent-lab` builds its OpenClaw image from a verified pinned upstream source commit. The upstream GHCR image is useful reference material, but it is not a trust anchor for this integration. -M1 does not run OpenClaw, does not create a Compose runtime profile, does not onboard, and does not add model/provider access or secrets. +This scaffold does not run OpenClaw, does not create a Compose runtime profile, does not onboard, and does not add model/provider access or secrets. -The v0 image preserves upstream runtime assumptions until a later milestone proves safe overrides: +The image preserves upstream runtime assumptions until safe overrides are proven: - user: `node`, uid `1000` - config: `/home/node/.openclaw` @@ -18,9 +18,9 @@ The v0 image preserves upstream runtime assumptions until a later milestone prov ## Hard Boundaries For Future Runtime Work -The locked v0 runtime must have no Docker socket, no host home mounts, no broad host binds, no public bind, no `host.docker.internal`, no browser binaries, no Docker CLI, no messaging channels, no MCP servers, no plugin or skill install surface, no exec/shell/process tools, and no secrets in Compose environment. +The locked runtime must have no Docker socket, no host home mounts, no broad host binds, no public bind, no `host.docker.internal`, no browser binaries, no Docker CLI, no messaging channels, no MCP servers, no plugin or skill install surface, no exec/shell/process tools, and no secrets in Compose environment. -OpenClaw sandboxing stays disabled in v0 because the Docker backend requires Docker socket access. +OpenClaw sandboxing stays disabled because the Docker backend requires Docker socket access. ## Source Fetch Workflow @@ -38,8 +38,8 @@ Build the local development image without running it: ./images/openclaw/build.sh ``` -## Next Milestones +## Planned Work -- M2: build verification, image inspection, SBOM, and vulnerability scan. -- M3: schema-verified locked capability config and fail-closed preflight. -- M4: hardened Compose gateway profile on a dedicated internal network. +- Build verification, image inspection, SBOM, and vulnerability scan. +- Schema-verified locked capability config and fail-closed preflight. +- Hardened Compose gateway profile on a dedicated internal network. diff --git a/policy/carveout.patterns b/policy/carveout.patterns index 2e1613d..8e86c69 100644 --- a/policy/carveout.patterns +++ b/policy/carveout.patterns @@ -1,7 +1,7 @@ # policy/carveout.patterns — destructive / integrity carve-out (denied under autonomy). # Read by tools/pretooluse-guard.sh. Doctrine: doctrine/destructive-ops.md. # This is the THINNEST enforcement layer (raw-string regex, no credential wall, shim covers only -# git/gh). It reliably stops accidents and casual evasion under autonomy (its D6 job); it is NOT +# git/gh). It reliably stops accidents and casual evasion under autonomy; it is NOT # adversary-proof — a hostile agent is contained by the sandbox, not by these patterns. # --- history rewrite / irreversible git --- diff --git a/scripts/dev/guard-diff b/scripts/dev/guard-diff index a133f1a..37f7d45 100755 --- a/scripts/dev/guard-diff +++ b/scripts/dev/guard-diff @@ -41,7 +41,7 @@ done # Optional repo-specific guard files: # .devguard/forbid-default.txt -# .devguard/forbid-M2.txt +# .devguard/forbid-release.txt for gf in ".devguard/forbid-default.txt" ".devguard/forbid-${scope}.txt"; do if test -f "$gf"; then while IFS= read -r pat; do diff --git a/scripts/dev/harvest-allowlist b/scripts/dev/harvest-allowlist index a79c2e4..195658a 100755 --- a/scripts/dev/harvest-allowlist +++ b/scripts/dev/harvest-allowlist @@ -21,7 +21,7 @@ printf '%s\n' "$log" | awk -v today="$(date -u +%Y-%m-%d)" ' print "# Squid egress harvest -- candidate allowlist recipe (REVIEW ONLY; never auto-applied)." print "# Generated: " today print "# NOTE: only proxy-mediated attempts appear here. Raw direct (non-proxy) attempts are" - print "# route-blocked by the internal network and are NOT logged in v0." + print "# route-blocked by the internal network and are NOT logged." print "#" } { diff --git a/scripts/doctor b/scripts/doctor index 208d6b0..ffc2680 100755 --- a/scripts/doctor +++ b/scripts/doctor @@ -131,7 +131,7 @@ else pass ".env.local has no obvious secret-looking assignments" fi -if (cd "$REPO_ROOT" && git grep -I -n -E '([A-Z0-9_]*(TOKEN|SECRET|API[_-]?KEY|PRIVATE[_-]?KEY|ACCESS[_-]?KEY)[A-Z0-9_]*[[:space:]]*[:=][[:space:]]*["'\'']?[A-Za-z0-9_./+=-]{16,}|BEGIN (RSA |OPENSSH |EC |DSA |)PRIVATE KEY)' -- . ':!PLAN.md' >/dev/null); then +if (cd "$REPO_ROOT" && git grep -I -n -E '([A-Z0-9_]*(TOKEN|SECRET|API[_-]?KEY|PRIVATE[_-]?KEY|ACCESS[_-]?KEY)[A-Z0-9_]*[[:space:]]*[:=][[:space:]]*["'\'']?[A-Za-z0-9_./+=-]{16,}|BEGIN (RSA |OPENSSH |EC |DSA |)PRIVATE KEY)' -- . >/dev/null); then fail "tracked source contains obvious secret-looking assignments or private keys" else pass "no obvious tracked secret patterns" diff --git a/tests/agent/agent-policy-checklist.md b/tests/agent/agent-policy-checklist.md index 31057ed..2fd43ef 100644 --- a/tests/agent/agent-policy-checklist.md +++ b/tests/agent/agent-policy-checklist.md @@ -1,4 +1,4 @@ -# Agent policy verification checklist (D8) +# Agent policy verification checklist Two layers: - **Tool-agnostic [probe]:** `bash tests/agent/policy-verify.sh` (guard, shims, token budget, @@ -49,5 +49,5 @@ printf '{"tool_input":{"command":"git push origin HEAD"}}' | tools/pretooluse-gu ``` ## Notes / known exceptions -- **Codex `git fetch` (#6) is N/A by design** (network-off); refresh remote outside the session (see `docs/agent-config.md`). Pending scope-D4 sign-off. +- **Codex `git fetch` (#6) is N/A by design** (network-off); refresh remote outside the session (see `docs/agent-config.md`). - The string-matching guard is **defense-in-depth**; argv-level shims (`tools/bin`) cover variable-indirection evasions; **containment is the real boundary**. diff --git a/tests/agent/policy-verify.sh b/tests/agent/policy-verify.sh index 7152a2d..22e7d45 100755 --- a/tests/agent/policy-verify.sh +++ b/tests/agent/policy-verify.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# tests/agent/policy-verify.sh — D8 tool-agnostic verification harness. +# tests/agent/policy-verify.sh — tool-agnostic verification harness. # Runs the guard/shim/token/generator/wiring/doctrine [probe] checks that don't need a live tool. # Per-tool LIVE checks (no-prompt loop, guard-fired, trust) are in agent-policy-checklist.md. # Tolerant of not-yet-built adapters (SKIP, not FAIL). Run: bash tests/agent/policy-verify.sh @@ -22,18 +22,18 @@ probe_cmd() { else [ "$rc" -eq 0 ] && pass "$name" || fail "$name (want rc0, got $rc)"; fi } -echo "== D10 token budget ==" +echo "== token budget ==" n=$(wc -c < AGENTS.md) [ "$n" -le 6000 ] && pass "AGENTS.md <= 6000 bytes ($n)" || fail "AGENTS.md token budget ($n/6000)" -echo "== D4/D6 guard unit matrix (delegate) ==" +echo "== guard unit matrix (delegate) ==" if bash tests/guard/pretooluse-cases.sh >/tmp/pol_guard.out 2>&1; then pass "tests/guard/pretooluse-cases.sh ($(grep -c '^PASS' /tmp/pol_guard.out) cases)" else fail "tests/guard/pretooluse-cases.sh — see /tmp/pol_guard.out" fi -echo "== D4 guard-fired & adversarial stdin (string forms) ==" +echo "== guard-fired & adversarial stdin (string forms) ==" for c in 'git push' 'git push origin HEAD' 'git -C . push' 'sh -c "git push"' 'env git push' \ 'nohup git push &' 'python3 -c "import subprocess;subprocess.run([\"git\",\"push\"])"' \ 'gh pr create' 'gh api -X POST repos/o/r/pulls' 'git pull' 'git merge origin/main' \ @@ -43,7 +43,7 @@ done probe_cmd allow "control: local merge feature-x" 'git merge feature-x' probe_cmd allow "control: local rebase main" 'git rebase main' -echo "== D4 shim adversarial (variable indirection — argv level) ==" +echo "== shim adversarial (variable indirection — argv level) ==" if [ -x tools/bin/git ]; then for c in 'g=push; git $g' 'm=merge; git $m origin/main'; do rc=0; out="$(PATH="$PWD/tools/bin:$PATH" bash -c "$c" 2>&1)" || rc=$? @@ -56,29 +56,29 @@ else skip "tools/bin/git shim missing" fi -echo "== D6/D9 protected-path edit backstop ==" +echo "== protected-path edit backstop ==" printf '{"tool_name":"Edit","tool_input":{"file_path":"doctrine/git-workflow.md"}}' \ | env -u AGENT_LAB_MAINTENANCE "$guard" >/dev/null 2>&1 && fail "doctrine edit not blocked" || pass "doctrine edit blocked (no maint)" printf '{"tool_name":"Edit","tool_input":{"file_path":"doctrine/git-workflow.md"}}' \ | env AGENT_LAB_MAINTENANCE=1 "$guard" >/dev/null 2>&1 && pass "doctrine edit allowed (maint=1)" || fail "maint=1 did not allow doctrine edit" -echo "== D6 Codex PermissionRequest approver ==" +echo "== Codex PermissionRequest approver ==" if [ -x tools/codex-permission-request.sh ]; then appr() { printf '{"tool_name":"Bash","tool_input":{"command":%s}}' "$(jq -Rn --arg c "$1" '$c')" | tools/codex-permission-request.sh 2>/dev/null; } appr 'git commit -m x' | grep -q '"behavior": *"allow"' && pass "approver allows commit" || fail "approver should allow commit" appr 'git push' | grep -q '"behavior": *"deny"' && pass "approver denies push" || fail "approver should deny push" else - skip "tools/codex-permission-request.sh not built yet (Phase 5)" + skip "tools/codex-permission-request.sh not built yet" fi -echo "== D1/D2 generator: idempotent + valid ==" +echo "== generator: idempotent + valid ==" if [ -x tools/render-adapters.sh ]; then jq -e . .claude/settings.json >/dev/null 2>&1 && pass "Claude settings.json valid JSON" || fail "Claude settings.json invalid JSON" else skip "tools/render-adapters.sh missing" fi -echo "== D1 wiring: PreToolUse hooks point at the one guard ==" +echo "== wiring: PreToolUse hooks point at the one guard ==" for f in .claude/settings.json .codex/hooks.json .grok/hooks/git-policy.json; do if [ -f "$f" ]; then grep -q 'pretooluse-guard.sh' "$f" && pass "wiring: $f -> guard" || fail "wiring: $f missing guard ref" @@ -87,7 +87,7 @@ for f in .claude/settings.json .codex/hooks.json .grok/hooks/git-policy.json; do fi done -echo "== D9 doctrine: TL;DR + index 1:1 ==" +echo "== doctrine: TL;DR + index 1:1 ==" ok=1; for f in doctrine/*.md; do l=$(grep -vE '^[[:space:]]*$' "$f" | grep -vE '^#' | head -1) case "$l" in "TL;DR:"*) ;; *) ok=0; echo " $f missing TL;DR";; esac diff --git a/tests/egress/README.md b/tests/egress/README.md index 369ddb2..1ce06ca 100644 --- a/tests/egress/README.md +++ b/tests/egress/README.md @@ -37,6 +37,6 @@ After allowlisted mode is verified, the script stops `egress-proxy` and reruns a ## Not Yet Proven -SNI mismatch protection is not implemented in v0. The test output includes a `NOT_IMPLEMENTED` line for this case so it is visible rather than silently skipped. M3 should add validated Squid peek/splice behavior or a different proven SNI control. +SNI mismatch protection is not implemented. The test output includes a `NOT_IMPLEMENTED` line for this case so it is visible rather than silently skipped. A future change should add validated Squid peek/splice behavior or a different proven SNI control. -Raw direct egress attempts are blocked by the internal network but are not logged in v0. Only proxy-mediated attempts are logged by Squid. +Raw direct egress attempts are blocked by the internal network but are not logged. Only proxy-mediated attempts are logged by Squid. diff --git a/tests/egress/cases.sh b/tests/egress/cases.sh index 3c54333..16c45cf 100755 --- a/tests/egress/cases.sh +++ b/tests/egress/cases.sh @@ -140,7 +140,7 @@ run_allowlisted() { expect_success "Docker socket is absent" docker_socket_absent expect_success "root filesystem write fails" root_filesystem_read_only expect_success "explicit tmpfs path is writable" tmp_is_writable - todo "SNI mismatch protection is M3: Squid CONNECT host allowlisting is enforced, TLS SNI peek/splice is not enabled" + todo "SNI mismatch protection not implemented: Squid CONNECT host allowlisting is enforced, TLS SNI peek/splice is not enabled" } run_fail_closed() { diff --git a/tools/render-adapters.sh b/tools/render-adapters.sh index b27084d..0b644de 100755 --- a/tools/render-adapters.sh +++ b/tools/render-adapters.sh @@ -14,7 +14,7 @@ # # Codex notes: execpolicy is TOKEN-prefix, so path-prefixes (./scripts/dev/) and remote refs # (origin/*) cannot be expressed as rules — the guard enforces those; workspace-write auto-runs -# in-workspace commands. 'git fetch' is omitted for Codex (network-off by design, v1). +# in-workspace commands. 'git fetch' is omitted for Codex (network-off by design). # # Idempotent. Run as a maintenance task: AGENT_LAB_MAINTENANCE=1 tools/render-adapters.sh set -euo pipefail @@ -86,12 +86,12 @@ emit_codex() { echo "# Codex execpolicy: allow local git + forbid remote git/PR. The PreToolUse guard" echo "# (tools/pretooluse-guard.sh) is the AUTHORITATIVE denier; these are belt-and-suspenders." echo "# Token-based: path-prefixes and origin/* refs are guard-enforced, not expressible here." - echo "# CODEX_V1: 'git fetch' intentionally omitted — workspace-write sandbox is network-off." + echo "# 'git fetch' intentionally omitted — workspace-write sandbox is network-off." echo echo "# -- allow: local git (clean token prefixes only; workspace-write auto-runs the rest) --" for c in "${ALLOW[@]}"; do case "$c" in - "git fetch") echo "# CODEX_V1: [\"git\",\"fetch\"] N/A — network-off" ;; + "git fetch") echo "# [\"git\",\"fetch\"] N/A — network-off" ;; git\ *) printf 'prefix_rule(pattern = [%s], decision = "allow", justification = "agent-lab local-git set")\n' "$(toks "$c")" ;; shellcheck) printf 'prefix_rule(pattern = ["shellcheck"], decision = "allow", justification = "agent-lab lint")\n' ;; esac