relay-shell 0.2.0__tar.gz → 0.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {relay_shell-0.2.0 → relay_shell-0.3.0}/.env.example +13 -0
- {relay_shell-0.2.0 → relay_shell-0.3.0}/.github/workflows/ci.yml +2 -2
- {relay_shell-0.2.0 → relay_shell-0.3.0}/.github/workflows/codeql.yml +1 -1
- relay_shell-0.3.0/.github/workflows/docs.yml +64 -0
- {relay_shell-0.2.0 → relay_shell-0.3.0}/.github/workflows/gitleaks.yml +1 -1
- {relay_shell-0.2.0 → relay_shell-0.3.0}/.github/workflows/nightly-fuzz.yml +2 -2
- {relay_shell-0.2.0 → relay_shell-0.3.0}/.github/workflows/pip-audit.yml +2 -2
- {relay_shell-0.2.0 → relay_shell-0.3.0}/.github/workflows/release.yml +4 -4
- {relay_shell-0.2.0 → relay_shell-0.3.0}/.github/workflows/sbom.yml +3 -3
- {relay_shell-0.2.0 → relay_shell-0.3.0}/.gitignore +3 -0
- {relay_shell-0.2.0 → relay_shell-0.3.0}/.hypothesis/constants/060c4657af60692d +1 -1
- {relay_shell-0.2.0 → relay_shell-0.3.0}/.hypothesis/constants/06d9884c28b2a63f +1 -1
- {relay_shell-0.2.0 → relay_shell-0.3.0}/.hypothesis/constants/09f931d5fb70626a +1 -1
- relay_shell-0.2.0/.hypothesis/constants/eb76034b3e66dae0 → relay_shell-0.3.0/.hypothesis/constants/0f0449cb9acd7923 +2 -2
- relay_shell-0.2.0/.hypothesis/constants/0a19132992ca9818 → relay_shell-0.3.0/.hypothesis/constants/0fbd7313047d9e52 +1 -1
- relay_shell-0.2.0/.hypothesis/constants/45397debbdfd60bc → relay_shell-0.3.0/.hypothesis/constants/29d0bc1435e72910 +1 -1
- {relay_shell-0.2.0 → relay_shell-0.3.0}/.hypothesis/constants/29ed694dbb01fab5 +1 -1
- relay_shell-0.3.0/.hypothesis/constants/31cd5feb5b48ec66 +4 -0
- relay_shell-0.2.0/.hypothesis/constants/6cf4f480c0f10b72 → relay_shell-0.3.0/.hypothesis/constants/4017e2a6f3ee7cfd +1 -1
- relay_shell-0.3.0/.hypothesis/constants/877af75780422922 +4 -0
- relay_shell-0.3.0/.hypothesis/constants/8967c8193f2aab55 +4 -0
- {relay_shell-0.2.0 → relay_shell-0.3.0}/.hypothesis/constants/95970940d76c226a +1 -1
- {relay_shell-0.2.0 → relay_shell-0.3.0}/.hypothesis/constants/a5fe03a1c421948b +1 -1
- relay_shell-0.3.0/.hypothesis/constants/c7900d7ab9e5fea7 +4 -0
- {relay_shell-0.2.0 → relay_shell-0.3.0}/.hypothesis/constants/d78536b0c96f72a7 +1 -1
- relay_shell-0.2.0/.hypothesis/constants/a3d3e5c3935b6ab5 → relay_shell-0.3.0/.hypothesis/constants/d8887ef76582104d +1 -1
- relay_shell-0.3.0/.hypothesis/constants/e562d9f2652151f0 +4 -0
- relay_shell-0.3.0/.hypothesis/constants/f4684eb785e4fb5b +4 -0
- relay_shell-0.2.0/.hypothesis/constants/0eb061c63a8794f0 → relay_shell-0.3.0/.hypothesis/constants/f7f625a7f9fbf8e6 +1 -1
- {relay_shell-0.2.0 → relay_shell-0.3.0}/.hypothesis/constants/f947d359e3d0041d +1 -1
- {relay_shell-0.2.0 → relay_shell-0.3.0}/.hypothesis/unicode_data/16.0.0/charmap.json.gz +0 -0
- {relay_shell-0.2.0 → relay_shell-0.3.0}/.pre-commit-config.yaml +1 -1
- {relay_shell-0.2.0 → relay_shell-0.3.0}/BACKLOG.md +57 -0
- {relay_shell-0.2.0 → relay_shell-0.3.0}/CHANGELOG.md +227 -1
- {relay_shell-0.2.0 → relay_shell-0.3.0}/PKG-INFO +10 -7
- {relay_shell-0.2.0 → relay_shell-0.3.0}/README.md +6 -5
- relay_shell-0.3.0/audit/2026-07-15-adversarial-engagement.md +72 -0
- relay_shell-0.3.0/audit/2026-07-15-engagement.md +190 -0
- relay_shell-0.3.0/audit/2026-07-15-release-v0.3.0.md +133 -0
- relay_shell-0.3.0/docs/_config.yml +29 -0
- {relay_shell-0.2.0 → relay_shell-0.3.0}/docs/adr/0005-codebase-validation.md +2 -2
- relay_shell-0.3.0/docs/adr/0009-tier3-confirmation-broker.md +220 -0
- relay_shell-0.3.0/docs/adr/0010-rollback-verify-broker.md +173 -0
- relay_shell-0.3.0/docs/adr/0011-windows-openssh-powershell.md +199 -0
- {relay_shell-0.2.0 → relay_shell-0.3.0}/docs/adr/README.md +4 -1
- {relay_shell-0.2.0 → relay_shell-0.3.0}/docs/architecture.md +10 -1
- {relay_shell-0.2.0 → relay_shell-0.3.0}/docs/audit-shipper.md +1 -1
- {relay_shell-0.2.0 → relay_shell-0.3.0}/docs/auth.md +5 -5
- {relay_shell-0.2.0 → relay_shell-0.3.0}/docs/deployment.md +148 -6
- {relay_shell-0.2.0 → relay_shell-0.3.0}/docs/index.md +1 -1
- {relay_shell-0.2.0 → relay_shell-0.3.0}/docs/runbook.md +104 -34
- {relay_shell-0.2.0 → relay_shell-0.3.0}/docs/tools.md +32 -8
- relay_shell-0.3.0/mkdocs.yml +106 -0
- {relay_shell-0.2.0 → relay_shell-0.3.0}/pyproject.toml +4 -2
- relay_shell-0.3.0/requirements-docs.txt +5 -0
- {relay_shell-0.2.0 → relay_shell-0.3.0}/requirements.txt +6 -6
- {relay_shell-0.2.0 → relay_shell-0.3.0}/src/relay_shell/__init__.py +1 -1
- {relay_shell-0.2.0 → relay_shell-0.3.0}/src/relay_shell/audit.py +12 -1
- {relay_shell-0.2.0 → relay_shell-0.3.0}/src/relay_shell/auth/oauth.py +24 -0
- relay_shell-0.3.0/src/relay_shell/broker.py +189 -0
- {relay_shell-0.2.0 → relay_shell-0.3.0}/src/relay_shell/config.py +10 -0
- {relay_shell-0.2.0 → relay_shell-0.3.0}/src/relay_shell/metrics.py +7 -1
- {relay_shell-0.2.0 → relay_shell-0.3.0}/src/relay_shell/patterns.py +168 -10
- {relay_shell-0.2.0 → relay_shell-0.3.0}/src/relay_shell/policy.py +8 -0
- {relay_shell-0.2.0 → relay_shell-0.3.0}/src/relay_shell/redaction.py +43 -9
- {relay_shell-0.2.0 → relay_shell-0.3.0}/src/relay_shell/server.py +238 -25
- {relay_shell-0.2.0 → relay_shell-0.3.0}/src/relay_shell/sessions.py +42 -21
- {relay_shell-0.2.0 → relay_shell-0.3.0}/src/relay_shell/sshpool.py +227 -123
- {relay_shell-0.2.0 → relay_shell-0.3.0}/tests/test_audit_tail_tool.py +68 -0
- relay_shell-0.3.0/tests/test_broker.py +302 -0
- {relay_shell-0.2.0 → relay_shell-0.3.0}/tests/test_oauth.py +79 -0
- {relay_shell-0.2.0 → relay_shell-0.3.0}/tests/test_patterns.py +208 -0
- {relay_shell-0.2.0 → relay_shell-0.3.0}/tests/test_policy.py +37 -0
- {relay_shell-0.2.0 → relay_shell-0.3.0}/tests/test_redaction.py +121 -1
- {relay_shell-0.2.0 → relay_shell-0.3.0}/tests/test_server.py +2 -1
- {relay_shell-0.2.0 → relay_shell-0.3.0}/tests/test_sessions.py +37 -0
- {relay_shell-0.2.0 → relay_shell-0.3.0}/tests/test_ssh_integration.py +59 -0
- {relay_shell-0.2.0 → relay_shell-0.3.0}/tests/test_sshpool_unit.py +233 -0
- {relay_shell-0.2.0 → relay_shell-0.3.0}/tests/test_stdio_e2e.py +1 -1
- {relay_shell-0.2.0 → relay_shell-0.3.0}/tests/test_tool_wrappers.py +63 -3
- relay_shell-0.2.0/.hypothesis/constants/bef274fdff8cb14f +0 -4
- relay_shell-0.2.0/.hypothesis/constants/c0cd33c96d9b3122 +0 -4
- relay_shell-0.2.0/.hypothesis/constants/c17f8b18040c167e +0 -4
- relay_shell-0.2.0/.hypothesis/constants/c83a8d1a7bc9e18d +0 -4
- relay_shell-0.2.0/.hypothesis/constants/d44e4bb8fa74f2f1 +0 -4
- relay_shell-0.2.0/docs/_config.yml +0 -18
- {relay_shell-0.2.0 → relay_shell-0.3.0}/.claude/settings.json +0 -0
- {relay_shell-0.2.0 → relay_shell-0.3.0}/.github/CODEOWNERS +0 -0
- {relay_shell-0.2.0 → relay_shell-0.3.0}/.github/ISSUE_TEMPLATE/bug.md +0 -0
- {relay_shell-0.2.0 → relay_shell-0.3.0}/.github/ISSUE_TEMPLATE/feature.md +0 -0
- {relay_shell-0.2.0 → relay_shell-0.3.0}/.github/ISSUE_TEMPLATE/security.md +0 -0
- {relay_shell-0.2.0 → relay_shell-0.3.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {relay_shell-0.2.0 → relay_shell-0.3.0}/.github/workflows/dependency-review.yml +0 -0
- {relay_shell-0.2.0 → relay_shell-0.3.0}/.gitleaks.toml +0 -0
- {relay_shell-0.2.0 → relay_shell-0.3.0}/.hypothesis/.gitignore +0 -0
- {relay_shell-0.2.0 → relay_shell-0.3.0}/AGENTS.md +0 -0
- {relay_shell-0.2.0 → relay_shell-0.3.0}/CLAUDE.md +0 -0
- {relay_shell-0.2.0 → relay_shell-0.3.0}/CODE_OF_CONDUCT.md +0 -0
- {relay_shell-0.2.0 → relay_shell-0.3.0}/CONTRIBUTING.md +0 -0
- {relay_shell-0.2.0 → relay_shell-0.3.0}/LICENSE +0 -0
- {relay_shell-0.2.0 → relay_shell-0.3.0}/NOTICE +0 -0
- {relay_shell-0.2.0 → relay_shell-0.3.0}/SECURITY.md +0 -0
- {relay_shell-0.2.0 → relay_shell-0.3.0}/audit/00-inventory.md +0 -0
- {relay_shell-0.2.0 → relay_shell-0.3.0}/audit/01-baseline.md +0 -0
- {relay_shell-0.2.0 → relay_shell-0.3.0}/audit/02-security-findings.md +0 -0
- {relay_shell-0.2.0 → relay_shell-0.3.0}/audit/03-final-report.md +0 -0
- {relay_shell-0.2.0 → relay_shell-0.3.0}/audit/2026-05-27-engagement.md +0 -0
- {relay_shell-0.2.0 → relay_shell-0.3.0}/audit/2026-06-01-engagement.md +0 -0
- {relay_shell-0.2.0 → relay_shell-0.3.0}/audit/2026-06-21-adversarial-engagement.md +0 -0
- {relay_shell-0.2.0 → relay_shell-0.3.0}/audit/2026-06-21-engagement.md +0 -0
- {relay_shell-0.2.0 → relay_shell-0.3.0}/deploy/Caddyfile +0 -0
- {relay_shell-0.2.0 → relay_shell-0.3.0}/deploy/install-edge.sh +0 -0
- {relay_shell-0.2.0 → relay_shell-0.3.0}/deploy/install.sh +0 -0
- {relay_shell-0.2.0 → relay_shell-0.3.0}/deploy/logrotate/relay-shell +0 -0
- {relay_shell-0.2.0 → relay_shell-0.3.0}/deploy/systemd/relay-shell.service +0 -0
- {relay_shell-0.2.0 → relay_shell-0.3.0}/deploy/systemd/relay-shell.service.d/hardening.conf +0 -0
- {relay_shell-0.2.0 → relay_shell-0.3.0}/docs/adr/0001-runtime-and-sdk.md +0 -0
- {relay_shell-0.2.0 → relay_shell-0.3.0}/docs/adr/0002-no-sandbox-full-access.md +0 -0
- {relay_shell-0.2.0 → relay_shell-0.3.0}/docs/adr/0003-tiered-authority.md +0 -0
- {relay_shell-0.2.0 → relay_shell-0.3.0}/docs/adr/0004-edge-tls-automation.md +0 -0
- {relay_shell-0.2.0 → relay_shell-0.3.0}/docs/adr/0006-seccomp-notify-audit-channel.md +0 -0
- {relay_shell-0.2.0 → relay_shell-0.3.0}/docs/adr/0007-audit-hash-chain.md +0 -0
- {relay_shell-0.2.0 → relay_shell-0.3.0}/docs/adr/0008-operating-guidance-prompt.md +0 -0
- {relay_shell-0.2.0 → relay_shell-0.3.0}/renovate.json5 +0 -0
- {relay_shell-0.2.0 → relay_shell-0.3.0}/scripts/healthcheck.sh +0 -0
- {relay_shell-0.2.0 → relay_shell-0.3.0}/src/relay_shell/__main__.py +0 -0
- {relay_shell-0.2.0 → relay_shell-0.3.0}/src/relay_shell/auth/__init__.py +0 -0
- {relay_shell-0.2.0 → relay_shell-0.3.0}/src/relay_shell/errors.py +0 -0
- {relay_shell-0.2.0 → relay_shell-0.3.0}/src/relay_shell/inventory.py +0 -0
- {relay_shell-0.2.0 → relay_shell-0.3.0}/src/relay_shell/py.typed +0 -0
- {relay_shell-0.2.0 → relay_shell-0.3.0}/src/relay_shell/seccomp.py +0 -0
- {relay_shell-0.2.0 → relay_shell-0.3.0}/src/relay_shell/shelltools.py +0 -0
- {relay_shell-0.2.0 → relay_shell-0.3.0}/src/relay_shell/util.py +0 -0
- {relay_shell-0.2.0 → relay_shell-0.3.0}/src/relay_shell/verifier.py +0 -0
- {relay_shell-0.2.0 → relay_shell-0.3.0}/tests/conftest.py +0 -0
- {relay_shell-0.2.0 → relay_shell-0.3.0}/tests/test_audit.py +0 -0
- {relay_shell-0.2.0 → relay_shell-0.3.0}/tests/test_config.py +0 -0
- {relay_shell-0.2.0 → relay_shell-0.3.0}/tests/test_errors.py +0 -0
- {relay_shell-0.2.0 → relay_shell-0.3.0}/tests/test_fuzz.py +0 -0
- {relay_shell-0.2.0 → relay_shell-0.3.0}/tests/test_inventory.py +0 -0
- {relay_shell-0.2.0 → relay_shell-0.3.0}/tests/test_main.py +0 -0
- {relay_shell-0.2.0 → relay_shell-0.3.0}/tests/test_metrics.py +0 -0
- {relay_shell-0.2.0 → relay_shell-0.3.0}/tests/test_prompts.py +0 -0
- {relay_shell-0.2.0 → relay_shell-0.3.0}/tests/test_resources.py +0 -0
- {relay_shell-0.2.0 → relay_shell-0.3.0}/tests/test_seccomp.py +0 -0
- {relay_shell-0.2.0 → relay_shell-0.3.0}/tests/test_shell.py +0 -0
- {relay_shell-0.2.0 → relay_shell-0.3.0}/tests/test_ssh_fanout_tool.py +0 -0
- {relay_shell-0.2.0 → relay_shell-0.3.0}/tests/test_ssh_keyscan_tool.py +0 -0
- {relay_shell-0.2.0 → relay_shell-0.3.0}/tests/test_util.py +0 -0
- {relay_shell-0.2.0 → relay_shell-0.3.0}/tests/test_verifier.py +0 -0
|
@@ -25,6 +25,19 @@ RELAY_SHELL_POLICY_MODE=open
|
|
|
25
25
|
RELAY_SHELL_POLICY_DENY=
|
|
26
26
|
RELAY_SHELL_POLICY_ALLOW=
|
|
27
27
|
|
|
28
|
+
# --- Tier-3 confirmation broker (ADR 0009) ---
|
|
29
|
+
# Opt-in, default off. When true, a Tier-3 (IRREVERSIBLE) command that already
|
|
30
|
+
# passed the deny list and mode check is not executed on first request: the
|
|
31
|
+
# runner returns a single-use, TTL-bounded token (audited action=confirm_plan,
|
|
32
|
+
# no side effect) and the caller must arm it via the operation_confirm tool,
|
|
33
|
+
# then re-issue the exact same call (audited action=confirm_execute). This is an
|
|
34
|
+
# added safeguard, never a bypass — the deny list and mode policy still run
|
|
35
|
+
# first. Default off keeps the audit record byte-identical.
|
|
36
|
+
RELAY_SHELL_CONFIRM_TIER3=false
|
|
37
|
+
# How long a minted confirmation token stays valid before it must be re-planned.
|
|
38
|
+
# Seconds, 5..3600.
|
|
39
|
+
RELAY_SHELL_CONFIRM_TTL=120
|
|
40
|
+
|
|
28
41
|
# --- Audit ---
|
|
29
42
|
RELAY_SHELL_AUDIT_PATH=/var/log/relay-shell/audit.jsonl
|
|
30
43
|
RELAY_SHELL_AUDIT_STDERR=false
|
|
@@ -24,9 +24,9 @@ jobs:
|
|
|
24
24
|
python-version: ["3.12", "3.13", "3.14"]
|
|
25
25
|
name: check (py${{ matrix.python-version }})
|
|
26
26
|
steps:
|
|
27
|
-
- uses: actions/checkout@
|
|
27
|
+
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
|
28
28
|
|
|
29
|
-
- uses: actions/setup-python@
|
|
29
|
+
- uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6
|
|
30
30
|
with:
|
|
31
31
|
python-version: ${{ matrix.python-version }}
|
|
32
32
|
cache: "pip"
|
|
@@ -24,7 +24,7 @@ jobs:
|
|
|
24
24
|
|
|
25
25
|
steps:
|
|
26
26
|
- name: Checkout
|
|
27
|
-
uses: actions/checkout@
|
|
27
|
+
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
|
28
28
|
|
|
29
29
|
- name: Initialize CodeQL
|
|
30
30
|
uses: github/codeql-action/init@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
name: docs
|
|
2
|
+
|
|
3
|
+
# Build the MkDocs Material documentation site from docs/ and publish it to
|
|
4
|
+
# GitHub Pages. Requires a one-time repo setting:
|
|
5
|
+
# Settings -> Pages -> Build and deployment -> Source: "GitHub Actions"
|
|
6
|
+
# (this replaces the former Jekyll "Deploy from a branch" source).
|
|
7
|
+
|
|
8
|
+
on:
|
|
9
|
+
push:
|
|
10
|
+
branches: [main]
|
|
11
|
+
paths:
|
|
12
|
+
- "docs/**"
|
|
13
|
+
- "mkdocs.yml"
|
|
14
|
+
- "requirements-docs.txt"
|
|
15
|
+
- ".github/workflows/docs.yml"
|
|
16
|
+
workflow_dispatch:
|
|
17
|
+
|
|
18
|
+
# Least-privilege default; the deploy job adds only the Pages-specific scopes.
|
|
19
|
+
permissions:
|
|
20
|
+
contents: read
|
|
21
|
+
|
|
22
|
+
# One Pages deployment at a time; let an in-flight publish finish.
|
|
23
|
+
concurrency:
|
|
24
|
+
group: pages
|
|
25
|
+
cancel-in-progress: false
|
|
26
|
+
|
|
27
|
+
jobs:
|
|
28
|
+
build:
|
|
29
|
+
name: Build (MkDocs Material)
|
|
30
|
+
runs-on: ubuntu-latest
|
|
31
|
+
timeout-minutes: 10
|
|
32
|
+
steps:
|
|
33
|
+
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
|
34
|
+
- uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6
|
|
35
|
+
with:
|
|
36
|
+
python-version: "3.14"
|
|
37
|
+
cache: pip
|
|
38
|
+
cache-dependency-path: requirements-docs.txt
|
|
39
|
+
- name: Install MkDocs Material
|
|
40
|
+
run: |
|
|
41
|
+
python -m pip install --upgrade pip
|
|
42
|
+
pip install -r requirements-docs.txt
|
|
43
|
+
- name: Build site (strict)
|
|
44
|
+
run: mkdocs build --strict
|
|
45
|
+
- name: Upload Pages artifact
|
|
46
|
+
uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5
|
|
47
|
+
with:
|
|
48
|
+
path: site
|
|
49
|
+
|
|
50
|
+
deploy:
|
|
51
|
+
name: Deploy to GitHub Pages
|
|
52
|
+
needs: build
|
|
53
|
+
runs-on: ubuntu-latest
|
|
54
|
+
timeout-minutes: 10
|
|
55
|
+
permissions:
|
|
56
|
+
pages: write # publish the Pages deployment
|
|
57
|
+
id-token: write # OIDC token required by deploy-pages
|
|
58
|
+
environment:
|
|
59
|
+
name: github-pages
|
|
60
|
+
url: ${{ steps.deployment.outputs.page_url }}
|
|
61
|
+
steps:
|
|
62
|
+
- name: Deploy
|
|
63
|
+
id: deployment
|
|
64
|
+
uses: actions/deploy-pages@cd2ce8fcbc39b97be8ca5fce6e763baed58fa128 # v5
|
|
@@ -28,7 +28,7 @@ jobs:
|
|
|
28
28
|
timeout-minutes: 10
|
|
29
29
|
steps:
|
|
30
30
|
- name: Checkout (full history so the scan covers every commit)
|
|
31
|
-
uses: actions/checkout@
|
|
31
|
+
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
|
32
32
|
with:
|
|
33
33
|
fetch-depth: 0
|
|
34
34
|
|
|
@@ -22,9 +22,9 @@ jobs:
|
|
|
22
22
|
runs-on: ubuntu-latest
|
|
23
23
|
timeout-minutes: 30
|
|
24
24
|
steps:
|
|
25
|
-
- uses: actions/checkout@
|
|
25
|
+
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
|
26
26
|
|
|
27
|
-
- uses: actions/setup-python@
|
|
27
|
+
- uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6
|
|
28
28
|
with:
|
|
29
29
|
python-version: "3.14"
|
|
30
30
|
cache: "pip"
|
|
@@ -25,9 +25,9 @@ jobs:
|
|
|
25
25
|
runs-on: ubuntu-latest
|
|
26
26
|
timeout-minutes: 10
|
|
27
27
|
steps:
|
|
28
|
-
- uses: actions/checkout@
|
|
28
|
+
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
|
29
29
|
|
|
30
|
-
- uses: actions/setup-python@
|
|
30
|
+
- uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6
|
|
31
31
|
with:
|
|
32
32
|
python-version: "3.14"
|
|
33
33
|
cache: "pip"
|
|
@@ -66,7 +66,7 @@ jobs:
|
|
|
66
66
|
# without this `ref` would compare the tag against whatever version
|
|
67
67
|
# currently sits on the default branch and falsely reject a valid
|
|
68
68
|
# signed tag.
|
|
69
|
-
- uses: actions/checkout@
|
|
69
|
+
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
|
70
70
|
with:
|
|
71
71
|
ref: ${{ steps.tag.outputs.tag }}
|
|
72
72
|
fetch-depth: 0
|
|
@@ -138,11 +138,11 @@ jobs:
|
|
|
138
138
|
id-token: write
|
|
139
139
|
attestations: write
|
|
140
140
|
steps:
|
|
141
|
-
- uses: actions/checkout@
|
|
141
|
+
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
|
142
142
|
with:
|
|
143
143
|
ref: ${{ needs.verify.outputs.release_commit }}
|
|
144
144
|
|
|
145
|
-
- uses: actions/setup-python@
|
|
145
|
+
- uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6
|
|
146
146
|
with:
|
|
147
147
|
python-version: "3.14"
|
|
148
148
|
cache: "pip"
|
|
@@ -171,7 +171,7 @@ jobs:
|
|
|
171
171
|
# PyPI OIDC trusted publishing already provides L2; this step
|
|
172
172
|
# adds a verifier-checkable provenance record in Sigstore's
|
|
173
173
|
# public transparency log.
|
|
174
|
-
uses: actions/attest-build-provenance@
|
|
174
|
+
uses: actions/attest-build-provenance@0f67c3f4856b2e3261c31976d6725780e5e4c373 # v4
|
|
175
175
|
with:
|
|
176
176
|
subject-path: 'dist/*'
|
|
177
177
|
|
|
@@ -33,10 +33,10 @@ jobs:
|
|
|
33
33
|
attestations: write # CI-3: write the provenance attestation
|
|
34
34
|
steps:
|
|
35
35
|
- name: Checkout
|
|
36
|
-
uses: actions/checkout@
|
|
36
|
+
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
|
37
37
|
|
|
38
38
|
- name: Set up Python
|
|
39
|
-
uses: actions/setup-python@
|
|
39
|
+
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6
|
|
40
40
|
with:
|
|
41
41
|
python-version: "3.14"
|
|
42
42
|
cache: "pip"
|
|
@@ -117,7 +117,7 @@ jobs:
|
|
|
117
117
|
# commit produced this SBOM — so a consumer can verify the SBOM is the
|
|
118
118
|
# genuine output of this repo's CI (`gh attestation verify <file>
|
|
119
119
|
# --repo ${{ github.repository }}`) rather than a swapped artifact.
|
|
120
|
-
uses: actions/attest-build-provenance@
|
|
120
|
+
uses: actions/attest-build-provenance@0f67c3f4856b2e3261c31976d6725780e5e4c373 # v4
|
|
121
121
|
with:
|
|
122
122
|
subject-path: |
|
|
123
123
|
relay-shell-${{ steps.tag.outputs.name }}.cdx.json
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
# file: /home/runner/work/relay-shell/relay-shell/src/relay_shell/sshpool.py
|
|
2
|
-
# hypothesis_version: 6.
|
|
2
|
+
# hypothesis_version: 6.156.6
|
|
3
3
|
|
|
4
4
|
[65536, ', ', ':', 'D', 'ForwardHandle', 'HUP', 'INT', 'KILL', 'L', 'QUIT', 'R', 'SshPool', 'SshProcessTransport', 'TERM', 'a', 'accept-new', 'client_keys', 'config', 'connect_timeout', 'dynamic', 'fwd', 'id', 'ignore', 'keepalive_interval', 'kind', 'known_hosts', 'listen_port', 'local', 'openssh', 'port', 'remote', 'replace', 'socks', 'spec', 'ssh_idle_timeout', 'strict', 'target', 'tunnel', 'username', 'utf-8', 'xterm-256color', '~/.ssh/known_hosts']
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
# file: /home/runner/work/relay-shell/relay-shell/src/relay_shell/sessions.py
|
|
2
|
-
# hypothesis_version: 6.
|
|
2
|
+
# hypothesis_version: 6.156.6
|
|
3
3
|
|
|
4
4
|
[0.0, 65536, 'HHHH', 'LocalPtyTransport', 'Session', 'SessionRegistry', 'Transport', 'age_s', 'buffered', 'closed', 'dropped', 'exit_code', 'id', 'idle_s', 'kind', 'produced', 'replace', 'sh', 'size', 'ssh', 'title', 'utf-8']
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
# file: /home/runner/work/relay-shell/relay-shell/src/relay_shell/verifier.py
|
|
2
|
-
# hypothesis_version: 6.
|
|
2
|
+
# hypothesis_version: 6.156.6
|
|
3
3
|
|
|
4
4
|
['/', '/etc/caddy/Caddyfile', 'ABSENT_TEMPLATE', 'Caddyfile', 'DRIFT', 'MISSING', 'OK', '_deploy', 'caddyfile', 'deploy', 'logrotate', 'relay_shell', 'systemd-hardening', 'systemd-unit', 'utf-8']
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
# file: /home/runner/work/relay-shell/relay-shell/src/relay_shell/policy.py
|
|
2
|
+
# hypothesis_version: 6.156.6
|
|
3
|
+
|
|
4
|
+
['Policy', 'PolicyDecision', 'Tier', 'audit_tail', 'classify', 'guarded', 'operation_confirm', 'permitted', 'readonly', 'server_info', 'session_kill', 'session_list', 'session_recv', 'session_resize', 'session_send', 'shell_exec', 'shell_script', 'shell_spawn', 'ssh_check', 'ssh_download', 'ssh_exec', 'ssh_fanout', 'ssh_forward', 'ssh_forward_list', 'ssh_hosts', 'ssh_spawn', 'ssh_upload']
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
# file: /home/runner/work/relay-shell/relay-shell/src/relay_shell/server.py
|
|
2
|
+
# hypothesis_version: 6.156.6
|
|
3
|
+
|
|
4
|
+
[0.0, 1.0, 2.0, 60.0, 100, 120, 128, 200, 256, 512, 1000, 1024, 2048, 4096, 8192, 32768, 65535, 65536, ',', '--', '-T', '-p', '-t', '.', '/bin/bash', '/bin/sudo', '/metrics', '/usr/bin/sudo', '/usr/local/bin/sudo', '0x', ':', 'GET', 'Host inventory', 'L', 'R', 'Relay', 'SIG', 'SSH config metadata', 'Single host spec', 'TERM', '[]', 'aliases', 'application/json', 'audit', 'audit_tail', 'auth', 'auth_server_provider', 'bash', 'build_server', 'cap', 'chain', 'client_id', 'close', 'cols', 'command', 'concurrency', 'confirm', 'confirm_execute', 'confirm_plan', 'confirm_required', 'connect_timeout', 'cwd', 'data', 'default_timeout', 'degraded', 'denied', 'denied_filter', 'dsa', 'ecdsa', 'echo ok', 'ed25519', 'enter', 'env_json', 'error', 'euid', 'exit_code', 'filter_version', 'format', 'forward_id', 'host', 'host_count', 'hosts', 'http', 'idle_timeout', 'instructions', 'interpreter', 'inventory', 'inventory_host', 'inventory_hosts', 'ipv4_mapped', 'is_root', 'json_response', 'jump', 'keepalive', 'key_path', 'key_types', 'known_hosts', 'known_hosts_default', 'limits', 'lines', 'local', 'max_forwards', 'max_output', 'max_output_hard', 'max_sessions', 'max_timeout', 'name', 'notify', 'ok', 'operating_guide', 'operation_confirm', 'output', 'path', 'pending', 'policy_mode', 'port', 'reason', 'relay-shell', 'relay_shell.server', 'remote', 'request_id', 'results', 'rows', 'rsa', 'rsa,ecdsa,ed25519', 'runtime', 'script', 'seccomp', 'server_info', 'session_id', 'session_kill', 'session_list', 'session_recv', 'session_resize', 'session_send', 'shell', 'shell_exec', 'shell_script', 'shell_spawn', 'signal', 'size', 'spec', 'ssh', 'ssh-config', 'ssh-keyscan', 'ssh_check', 'ssh_config', 'ssh_download', 'ssh_exec', 'ssh_fanout', 'ssh_forward', 'ssh_forward_close', 'ssh_forward_list', 'ssh_hosts', 'ssh_keyscan', 'ssh_spawn', 'ssh_upload', 'stateless_http', 'stdin', 'strict', 'sudo_binary', 'supported', 'tier', 'tier3', 'tier_filter', 'timeout', 'token_sha256', 'tool', 'tool_filter', 'transport', 'ttl', 'uid', 'use_shell', 'user', 'utf-8', 'version']
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
# file: /home/runner/work/relay-shell/relay-shell/src/relay_shell/seccomp.py
|
|
2
|
-
# hypothesis_version: 6.
|
|
2
|
+
# hypothesis_version: 6.156.6
|
|
3
3
|
|
|
4
4
|
[b'\x00', b'\x01', b'x', 5.0, 101, 105, 106, 113, 114, 117, 119, 155, 157, 161, 165, 166, 255, 257, 272, 308, 317, 322, 1000, 2143289344, 2147418112, 3221225534, 4294967295, '!', '-', '.', '/proc/self/status', '6Q', '@fall', 'CapEff:', 'HBBI', 'NOTIFIED_SYSCALLS', 'PlatformSupport', 'QI', 'QqiI', 'SeccompMonitor', 'SyscallEvent', '_listener_fd', '_stop_r', '_stop_w', 'allow', 'build_filter', 'chroot', 'clear_active', 'data', 'execve', 'execveat', 'filter', 'get_active', 'i', 'ja', 'jump', 'len', 'libc unavailable', 'libc.so.6', 'linux', 'mount', 'notif', 'notify', 'ok', 'open', 'openat', 'pass_fds', 'pivot_root', 'platform_support', 'prctl', 'preexec_fn', 'ptrace', 'relay-seccomp-notify', 'relay_shell.seccomp', 'relay_shell_seccomp', 'resp', 'set_active', 'setgid', 'setns', 'setregid', 'setresgid', 'setresuid', 'setreuid', 'setuid', 'stmt', 'syscall_name', 'umount2', 'unshare', 'utf-8', 'x86_64']
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
# file: /home/runner/work/relay-shell/relay-shell/src/relay_shell/inventory.py
|
|
2
|
-
# hypothesis_version: 6.
|
|
2
|
+
# hypothesis_version: 6.156.6
|
|
3
3
|
|
|
4
4
|
['#', '*?!', '=', '@', 'HostSpec', 'Inventory', 'explicit', 'host', 'hostname', 'identity_file', 'identityfile', 'inventory', 'jump', 'known_hosts', 'name', 'port', 'proxyjump', 'replace', 'source', 'ssh_config', 'user', 'utf-8']
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
# file: /home/runner/work/relay-shell/relay-shell/src/relay_shell/patterns.py
|
|
2
|
+
# hypothesis_version: 6.156.6
|
|
3
|
+
|
|
4
|
+
['11', 'PATTERNS_VERSION', 'PRIV_ESC_PATTERN', 'REDACTION_PATTERNS', 'TIER2_PATTERN', 'TIER3_PATTERN', 'URL_CREDS_PATTERN', '[REDACTED]', '\\bAKIA[0-9A-Z]{16}\\b', '\\g<prefix>[REDACTED]']
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
# file: /home/runner/work/relay-shell/relay-shell/src/relay_shell/auth/oauth.py
|
|
2
|
-
# hypothesis_version: 6.
|
|
2
|
+
# hypothesis_version: 6.156.6
|
|
3
3
|
|
|
4
4
|
[384, 448, 511, '.tmp', 'Bearer', 'FileOAuthProvider', 'build_auth_settings', 'client_id', 'clients.json', 'code', 'code_challenge', 'codes.json', 'expires_at', 'invalid_grant', 'make_oauth_provider', 'mcp:tools', 'redirect_uri', 'refresh:', 'resource', 'scopes', 'state', 'token', 'tokens.json', 'utf-8', 'w']
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
# file: /home/runner/work/relay-shell/relay-shell/src/relay_shell/audit.py
|
|
2
|
+
# hypothesis_version: 6.156.6
|
|
3
|
+
|
|
4
|
+
[b'\n', 384, 8192, '%(message)s', ',', '0', '1.0', '5', ':', '=', 'AUDIT %(message)s', 'AuditLogger', 'ChainResult', '\\', '\\=', '\\\\', '\\n', '\\r', '\\t', '\\|', 'action', 'args', 'audit', 'cap', 'cef', 'chain', 'client_id', 'denied', 'exit_code', 'jsonl', 'leef', 'nr', 'output_len', 'output_sha256', 'pid', 'prev', 'r', 'rb', 'relay-shell', 'relay_shell.audit', 'replace', 'request_id', 'seq', 'syscall', 'syscall_args', 'syscall_notify', 'tier', 'tool', 'tool-call', 'ts', 'utf-8', 'verify_chain', '|']
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
# file: /home/runner/work/relay-shell/relay-shell/src/relay_shell/config.py
|
|
2
|
-
# hypothesis_version: 6.
|
|
2
|
+
# hypothesis_version: 6.156.6
|
|
3
3
|
|
|
4
4
|
[120, 256, 300, 600, 900, 1024, 1800, 3600, 4096, 8080, 65535, 65536, 86400, 262144, 1048576, 2592000, 16777216, 134217728, '.env', '127.0.0.1', 'RELAY_SHELL_', 'Settings', 'accept-new', 'after', 'audit_format', 'cef', 'get_settings', 'guarded', 'http', 'ignore', 'jsonl', 'leef', 'open', 'policy_mode', 'readonly', 'ssh_known_hosts', 'stdio', 'strict', 'transport', 'utf-8', '~/.ssh/config']
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
# file: /home/runner/work/relay-shell/relay-shell/src/relay_shell/shelltools.py
|
|
2
|
-
# hypothesis_version: 6.
|
|
2
|
+
# hypothesis_version: 6.156.6
|
|
3
3
|
|
|
4
4
|
['-', '-s', '/bin/bash', '/bin/sh', '0', 'C.UTF-8', 'DEBIAN_FRONTEND', 'GIT_TERMINAL_PROMPT', 'LANG', 'PATH', 'bash', 'build_env', 'cwd', 'env', 'noninteractive', 'py', 'python', 'python3', 'replace', 'run_command', 'run_script', 'set -euo pipefail\n', 'sh', 'spawn_argv', 'start_new_session', 'stderr', 'stdin', 'stdout', 'utf-8']
|
|
Binary file
|
|
@@ -15,7 +15,7 @@ repos:
|
|
|
15
15
|
# Pin to a known-good ruff release. Update this `rev` and the
|
|
16
16
|
# `ruff>=` floor in `[project.optional-dependencies] dev` together
|
|
17
17
|
# so local pre-commit and `pip install -e ".[dev]"` agree.
|
|
18
|
-
rev: v0.15.
|
|
18
|
+
rev: v0.15.20
|
|
19
19
|
hooks:
|
|
20
20
|
- id: ruff
|
|
21
21
|
args: [--fix]
|
|
@@ -13,6 +13,38 @@ its kind is tracked (runbook §7 for capability/quality/ops/security-hardening,
|
|
|
13
13
|
|
|
14
14
|
Severity order within each section: low before info; smaller effort first.
|
|
15
15
|
|
|
16
|
+
## 2026-07-15 adversarial + performance pass
|
|
17
|
+
|
|
18
|
+
Findings from the red-team + profiling pass
|
|
19
|
+
([`audit/2026-07-15-adversarial-engagement.md`](audit/2026-07-15-adversarial-engagement.md)),
|
|
20
|
+
prompted by "is everything lean, secure, performant?".
|
|
21
|
+
|
|
22
|
+
Closed (engagement PR):
|
|
23
|
+
|
|
24
|
+
| ID | Sev | Title | Resolution |
|
|
25
|
+
|----|-----|-------|------------|
|
|
26
|
+
| RED-6 | HIGH | JSON-quoted-key secret leak in redaction | Quote-tolerant separator + linear char-class value run on the generic + AWS `secret_access_key` rules (`PATTERNS_VERSION` 8). Paired JSON-shape over/under-scrub tests. |
|
|
27
|
+
| RED-6a | HIGH | ReDoS introduced by the first RED-6 draft (lookahead O(n²)) | Replaced with a linear char-class matcher; 1 MB scan 165 s → 196 ms. Linearity regression test. |
|
|
28
|
+
| P1 | MED | Redaction scanned the full untruncated arg on the event loop | `_scrub_str` bounds the scan to `max_len + 16 KiB`; 1–4 MB arg now constant ~12 ms. Bounded-scan test. Lossless for every truncation-safe pattern — see RED-8 for the one quoted-value edge the original "lossless" claim missed. |
|
|
29
|
+
| POL-2 | MED | `TIER3_PATTERN` missed non-obfuscated `rm` long options | Bounded (`{0,16}`) long-option `rm` alternative; positive/negative + ReDoS-ceiling tests. |
|
|
30
|
+
| SSH-4 | MED | Deny-list blind to host for `ssh_exec`/`ssh_spawn`/`ssh_fanout`/`ssh_check` | Fold the destination host (canonical-IP widened) into their deny probes; wiring test. |
|
|
31
|
+
| BRK-3 | MED | Broker binding omitted SSH identity (`user`/`port`/`key_path`) | Added to `ssh_exec`/`ssh_spawn` audit_args → op-key binding + audit visibility; ADR 0009 follow-up; binding test. |
|
|
32
|
+
|
|
33
|
+
Closed (post-engagement follow-up review):
|
|
34
|
+
|
|
35
|
+
| ID | Sev | Title | Resolution |
|
|
36
|
+
|----|-----|-------|------------|
|
|
37
|
+
| RED-8 | MED | P1 scan-window leaked the tail of a quoted secret past the window | The CLI-flag rule's quoted-value branches required a closing quote and were length-unbounded, so a `--password="…"` value longer than `max_len + 16 KiB` lost its close quote to truncation → the quoted branch failed, the greedy bare fallback stopped at the first internal space, and the post-space tail leaked into the audit record (full-string redaction still caught it, so a P1 regression). Added unterminated-quote fallback branches consuming to end-of-line (`PATTERNS_VERSION` 9); well-formed values byte-identical. Authorization rule verified unaffected (its `$` branch already tolerates truncation). PoC + `test_red8_*` regressions + linearity check. |
|
|
38
|
+
| AUTH-3 | LOW | OAuth `exchange_*` re-checked `client_id` but not `expires_at` | `exchange_authorization_code` / `exchange_refresh_token` enforced expiry only via their `load_*` gate, not atomically at exchange — a record valid at load could lapse in the load→exchange window (or a caller could reach exchange without load) and still mint a token (PoC: an expired code and an expired refresh both minted access tokens). Added an `expires_at` re-check after the existing `client_id` re-check in both paths (raise `invalid_grant`, drop the stale record), mirroring the file's defense-in-depth pattern. Not exploitable in the normal SDK flow (load runs first); closes the TOCTOU window. Paired `test_exchange_*_refuses_expired_*` regressions. |
|
|
39
|
+
|
|
40
|
+
Open deferrals (low-priority perf; measure before acting — none on a hot path):
|
|
41
|
+
|
|
42
|
+
| ID | Item | Sev | Effort | Rationale | Owner role |
|
|
43
|
+
|----|------|-----|--------|-----------|------------|
|
|
44
|
+
| PERF-1 | `AuditLogger._emit` does a synchronous file write+flush on the event loop (the read path is already offloaded via `asyncio.to_thread`) | low | S | Local append is fast; offload only if a slow/networked audit sink shows up. Consistency, not a measured stall. | maintainer |
|
|
45
|
+
| PERF-2 | `SshPool._sweep_conns` is O(n) over cached connections under one lock per `connect()` | low | S | n is bounded by the connection cache (small); revisit only for very large fleets. | maintainer |
|
|
46
|
+
| PERF-3 | `Session.buffer` uses `bytearray` front-deletion (memmove) on overflow/drain | low | S | Buffer is bounded (`session_buffer_bytes`); a deque-of-chunks would avoid the memmove if it ever matters. | maintainer |
|
|
47
|
+
|
|
16
48
|
## Closed (follow-up work after the audit pass)
|
|
17
49
|
|
|
18
50
|
Resolved in the backlog-work PRs that followed the 2026-06-12 audit pass.
|
|
@@ -168,6 +200,31 @@ accepted-as-designed with Renovate evidence; see the Closed table above.)
|
|
|
168
200
|
|---|---|---|---|---|---|
|
|
169
201
|
| F-G2 | Branch protection on `main` — **fully resolved** (rules enumerated and hardened) | high (governance), now closed | S (done) | Prior packs (`audit/2026-06-01-engagement.md` §7.1) carried this as an open HIGH item ("`main` accepts direct pushes"). Resolved in two steps. 2026-06-12 (audit pass): the branches API reports `protected: true` [V]. 2026-06-12 (follow-up, via the Vertex-held `gh` credential): protection comes from ruleset `main-protection` (id 17307996; classic protection is unset), which enforced pull_request (0 approvals), non_fast_forward, deletion, and required_linear_history [V]. With operator confirmation (a T3 change per the operator's `github-vertex.md` contract), the ruleset gained `required_status_checks` — `check (py3.12)` / `check (py3.13)` / `check (py3.14)` / `gitleaks (secret scan)`, all bound to GitHub Actions, strict=false — and `required_signatures` (closing the prior pack's deferred **P2-3**). Verified effective post-change via `GET /repos/rmednitzer/relay-shell/rules/branches/main` [V]. pip-audit / dependency-review / CodeQL stay advisory by operator choice. | repo owner (executed with operator confirmation) |
|
|
170
202
|
|
|
203
|
+
## 2026-07-15 full audit + Vertex/Axiom comparison pass
|
|
204
|
+
|
|
205
|
+
Findings and lessons from the 2026-07-15 pass
|
|
206
|
+
([`audit/2026-07-15-engagement.md`](audit/2026-07-15-engagement.md)). The
|
|
207
|
+
scanner battery (pip-audit, trivy vuln+secret, bandit, semgrep, actionlint,
|
|
208
|
+
shellcheck) was clean and no pinned dependency carries a known CVE; there were
|
|
209
|
+
no P0/P1 findings. The pass compared `relay-shell` against two sibling MCP
|
|
210
|
+
control planes and turned the highest-value lesson (L1) into shipped code.
|
|
211
|
+
|
|
212
|
+
Closed (engagement PR):
|
|
213
|
+
|
|
214
|
+
| ID | Title | Resolution |
|
|
215
|
+
|---|---|---|
|
|
216
|
+
| L1 (broker) | No per-call confirmation for irreversible ops | **Closed** (this PR). Implemented the opt-in Tier-3 confirmation broker: [ADR 0009](docs/adr/0009-tier3-confirmation-broker.md), `src/relay_shell/broker.py` (100% covered), `operation_confirm` tool (contract 21 → 22), `action=confirm_plan`/`confirm_execute` audit markers, `server_info.confirm` block. Default-off byte-identical; layered after the deny/mode check. |
|
|
217
|
+
| DOC-1 | Living docs named the superseded pin (`mcp==1.27.2` / `asyncssh` 2.23.1) after Renovate moved it to `mcp==1.28.1` / `asyncssh==2.24.0` | **Closed** (this PR). Reconciled the README status line + compatibility matrix and the `docs/architecture.md` diagram; "last validated" → 2026-07-15. Frozen ADRs / prior engagement records left intact per convention. |
|
|
218
|
+
| ENV-1 | `.env.example` missing `RELAY_SHELL_CONFIRM_TIER3` / `RELAY_SHELL_CONFIRM_TTL` | **Closed** (PR #129). Added the two commented vars mirroring `docs/deployment.md` §8a (this session couldn't edit `.env*` paths; done in a follow-up). |
|
|
219
|
+
| DOC-6 (L3) | No deploy-host HIDS / config-drift guidance | **Closed** (DOC-6 PR). `docs/deployment.md` §3a "Host integrity + config-drift monitoring" documents etckeeper / AIDE / fail2ban / lynis as detection compensating for the intentionally-partial systemd confinement. Docs only. |
|
|
220
|
+
| AUD-1 (L2) | No in-band audit verify / correlate tool | **Closed as a split disposition** (AUD-1 PR), after evaluating the lesson against ADR 0007 + the actual audit schema. **Shipped:** read-only triage filters on `audit_tail` (`tool`/`tier`/`denied`, Tier 0, tool contract unchanged) — the operator-diagnostic use case that already justifies `audit_tail`. **Declined:** in-band chain-*verify* stays the CLI `--verify-audit` (ADR 0007 — the audited model must not drive verification of its own trail). **N/A:** correlate-by-input-`sha256` — relay-shell hashes the *output*, not the input, so there is no input hash to correlate on. |
|
|
221
|
+
|
|
222
|
+
Open deferrals (severity order; smaller effort first):
|
|
223
|
+
|
|
224
|
+
| ID | Item | Sev | Effort | Rationale / approach | Owner role |
|
|
225
|
+
|---|---|---|---|---|---|
|
|
226
|
+
| OPS-2 (L4) | `pip-audit` has no KEV/EPSS exploit prioritization | info | S | Lesson from the comparison: layer Known-Exploited / EPSS signal on top of the existing `pip-audit` gate for prioritization. Advisory only — `pip-audit` already fails closed. Low value for the small pinned set; revisit if the dependency set grows. | maintainer |
|
|
227
|
+
|
|
171
228
|
## Notes on items NOT added here
|
|
172
229
|
|
|
173
230
|
- No critical / high / medium **code** security findings were produced by
|