plugin-scanner 2.0.78__tar.gz → 2.0.79__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.
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/PKG-INFO +1 -1
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/pyproject.toml +1 -1
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/pyproject.toml.bak +1 -1
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/guard/adapters/codex.py +36 -12
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/guard/cli/commands.py +248 -10
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/guard/store.py +34 -2
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/version.py +1 -1
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/test_guard_codex_install.py +81 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/test_guard_runtime.py +261 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/.clusterfuzzlite/Dockerfile +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/.clusterfuzzlite/build.sh +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/.clusterfuzzlite/project.yaml +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/.clusterfuzzlite/requirements-atheris.txt +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/.dockerignore +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/.github/CODEOWNERS +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/.github/ISSUE_TEMPLATE/bug-report.yml +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/.github/ISSUE_TEMPLATE/feature-request.yml +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/.github/dependabot.yml +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/.github/workflows/ci.yml +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/.github/workflows/codeql.yml +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/.github/workflows/dependabot-uv-lock.yml +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/.github/workflows/fuzz.yml +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/.github/workflows/harness-smoke.yml +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/.github/workflows/publish.yml +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/.github/workflows/scorecard.yml +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/.gitignore +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/.pre-commit-hooks.yaml +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/CONTRIBUTING.md +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/Dockerfile +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/LICENSE +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/README.md +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/SECURITY.md +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/dashboard/index.html +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/dashboard/package.json +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/dashboard/pnpm-lock.yaml +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/dashboard/public/apple-touch-icon.png +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/dashboard/public/brand/Logo_Icon_Dark.png +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/dashboard/public/brand/Logo_Whole.png +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/dashboard/public/favicon-16x16.png +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/dashboard/public/favicon-32x32.png +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/dashboard/public/favicon.ico +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/dashboard/src/app.tsx +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/dashboard/src/approval-center-layout.tsx +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/dashboard/src/approval-center-primitives.tsx +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/dashboard/src/approval-center-utils.ts +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/dashboard/src/fleet-workspace.tsx +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/dashboard/src/guard-api.ts +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/dashboard/src/guard-demo.ts +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/dashboard/src/guard-types.ts +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/dashboard/src/main.tsx +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/dashboard/src/receipts-workspace.tsx +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/dashboard/src/runtime-overview.tsx +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/dashboard/src/settings-workspace.tsx +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/dashboard/src/styles.css +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/dashboard/src/vite-env.d.ts +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/dashboard/tsconfig.json +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/dashboard/vite.config.ts +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/docker-requirements.txt +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/docs/guard/approval-audit.md +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/docs/guard/architecture.md +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/docs/guard/get-started.md +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/docs/guard/harness-support.md +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/docs/guard/local-vs-cloud.md +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/docs/guard/testing-matrix.md +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/docs/trust/mcp-trust-draft.md +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/docs/trust/plugin-trust-draft.md +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/docs/trust/skill-trust-local.md +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/fuzzers/manifest_fuzzer.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/requirements.txt +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/schemas/plugin-quality.v1.json +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/schemas/scan-result.v1.json +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/schemas/verify-result.v1.json +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/__init__.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/action_runner.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/argparse_utils.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/checks/__init__.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/checks/best_practices.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/checks/claude.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/checks/code_quality.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/checks/ecosystem_common.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/checks/gemini.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/checks/manifest.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/checks/manifest_support.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/checks/marketplace.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/checks/mcp_security.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/checks/opencode.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/checks/operational_security.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/checks/security.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/checks/skill_security.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/cli.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/cli_ui.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/config.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/ecosystems/__init__.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/ecosystems/base.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/ecosystems/claude.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/ecosystems/codex.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/ecosystems/detect.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/ecosystems/gemini.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/ecosystems/opencode.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/ecosystems/registry.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/ecosystems/types.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/github_reporting.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/guard/__init__.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/guard/adapters/__init__.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/guard/adapters/antigravity.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/guard/adapters/base.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/guard/adapters/claude_code.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/guard/adapters/copilot.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/guard/adapters/cursor.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/guard/adapters/gemini.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/guard/adapters/hermes.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/guard/adapters/mcp_servers.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/guard/adapters/opencode.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/guard/adapters/opencode_artifacts.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/guard/advisory_model.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/guard/approvals.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/guard/bridge/__init__.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/guard/capabilities.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/guard/cli/__init__.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/guard/cli/approval_commands.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/guard/cli/bootstrap.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/guard/cli/connect_flow.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/guard/cli/install_commands.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/guard/cli/product.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/guard/cli/prompt.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/guard/cli/render.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/guard/cli/update_commands.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/guard/codex_config.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/guard/config.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/guard/consumer/__init__.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/guard/consumer/service.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/guard/daemon/__init__.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/guard/daemon/client.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/guard/daemon/manager.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/guard/daemon/server.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/guard/daemon/static/apple-touch-icon.png +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/guard/daemon/static/assets/guard-dashboard.js +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/guard/daemon/static/assets/index.css +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/guard/daemon/static/brand/Logo_Icon_Dark.png +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/guard/daemon/static/brand/Logo_Whole.png +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/guard/daemon/static/favicon-16x16.png +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/guard/daemon/static/favicon-32x32.png +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/guard/daemon/static/favicon.ico +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/guard/daemon/static/index.html +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/guard/edge_events.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/guard/incident.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/guard/launcher.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/guard/mcp_tool_calls.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/guard/models.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/guard/policy/__init__.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/guard/policy/engine.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/guard/protect.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/guard/proxy/__init__.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/guard/proxy/remote.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/guard/proxy/runtime_mcp.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/guard/proxy/stdio.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/guard/receipts/__init__.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/guard/receipts/manager.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/guard/redaction.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/guard/risk.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/guard/runtime/__init__.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/guard/runtime/runner.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/guard/runtime/secret_file_requests.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/guard/runtime/surface_server.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/guard/schemas/__init__.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/guard/schemas/consumer_mode.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/guard/schemas/guard_event_v1.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/guard/schemas/surface_server.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/guard/shims.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/guard/store_approvals.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/guard/store_connect.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/guard/types.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/integrations/__init__.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/integrations/cisco_mcp_scanner.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/integrations/cisco_skill_scanner.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/lint_fixes.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/marketplace_support.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/models.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/path_support.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/policy.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/quality_artifact.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/repo_detect.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/reporting.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/rules/__init__.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/rules/registry.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/rules/specs.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/scanner.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/submission.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/suppressions.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/trust_domain_scoring.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/trust_helpers.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/trust_mcp_scoring.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/trust_models.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/trust_plugin_scoring.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/trust_scoring.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/trust_skill_scoring.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/trust_specs.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/verification.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/__init__.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/conftest.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/fixtures/__init__.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/fixtures/bad-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/fixtures/bad-plugin/.mcp.json +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/fixtures/bad-plugin/secrets.js +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/fixtures/claude-plugin-good/.claude-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/fixtures/claude-plugin-good/LICENSE +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/fixtures/claude-plugin-good/README.md +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/fixtures/claude-plugin-good/SECURITY.md +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/fixtures/claude-plugin-good/hooks/hooks.json +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/fixtures/claude-plugin-good/skills/example/SKILL.md +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/fixtures/code-quality-bad/evil.js +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/fixtures/code-quality-bad/inject.js +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/fixtures/gemini-extension-good/GEMINI.md +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/fixtures/gemini-extension-good/LICENSE +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/fixtures/gemini-extension-good/README.md +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/fixtures/gemini-extension-good/SECURITY.md +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/fixtures/gemini-extension-good/commands/hello.toml +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/fixtures/gemini-extension-good/gemini-extension.json +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/fixtures/good-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/fixtures/good-plugin/.codexignore +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/fixtures/good-plugin/LICENSE +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/fixtures/good-plugin/README.md +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/fixtures/good-plugin/SECURITY.md +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/fixtures/good-plugin/assets/icon.svg +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/fixtures/good-plugin/assets/logo.svg +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/fixtures/good-plugin/assets/screenshot.svg +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/fixtures/good-plugin/skills/example/SKILL.md +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/fixtures/guard-codex-malicious-mcp/.codex/config.toml +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/fixtures/hermes-plugin-evil/config.yaml +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/fixtures/hermes-plugin-evil/mcp_servers.json +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/fixtures/hermes-plugin-evil/skills/security/malicious/SKILL.md +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/SKILL.md +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/references/api-setup.md +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/scripts/deploy.sh +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/fixtures/hermes-plugin-evil/skills/utils/benign/SKILL.md +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/fixtures/malformed-json/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/fixtures/malicious-skill-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/fixtures/malicious-skill-plugin/.codexignore +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/fixtures/malicious-skill-plugin/LICENSE +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/fixtures/malicious-skill-plugin/README.md +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/fixtures/malicious-skill-plugin/SECURITY.md +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/fixtures/malicious-skill-plugin/skills/leaky-skill/SKILL.md +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/fixtures/mcp-canary-server.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/fixtures/minimal-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/fixtures/missing-fields/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/fixtures/mit-license/LICENSE +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/fixtures/multi-ecosystem-repo/codex-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/fixtures/multi-ecosystem-repo/codex-plugin/LICENSE +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/fixtures/multi-ecosystem-repo/codex-plugin/README.md +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/fixtures/multi-ecosystem-repo/codex-plugin/SECURITY.md +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/fixtures/multi-ecosystem-repo/gemini-ext/README.md +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/fixtures/multi-ecosystem-repo/gemini-ext/gemini-extension.json +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/fixtures/multi-plugin-repo/.agents/plugins/marketplace.json +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/.codexignore +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/LICENSE +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/README.md +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/SECURITY.md +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/skills/example/SKILL.md +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/fixtures/multi-plugin-repo/plugins/beta-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/fixtures/multi-plugin-repo/plugins/beta-plugin/skills/example/SKILL.md +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/fixtures/no-version/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/fixtures/opencode-good/.opencode/commands/hello.md +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/fixtures/opencode-good/.opencode/plugins/example.ts +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/fixtures/opencode-good/LICENSE +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/fixtures/opencode-good/README.md +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/fixtures/opencode-good/SECURITY.md +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/fixtures/opencode-good/opencode.jsonc +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/fixtures/skills-missing-dir/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/fixtures/skills-no-frontmatter/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/fixtures/skills-no-frontmatter/skills/bad-skill/SKILL.md +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/fixtures/with-marketplace/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/fixtures/with-marketplace/marketplace-broken.json +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/fixtures/with-marketplace/marketplace.json +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/test-trust-scoring.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/test-trust-specs.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/test_action_runner.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/test_best_practices.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/test_cisco_install_surfaces.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/test_cli.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/test_code_quality.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/test_config.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/test_coverage_remaining.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/test_ecosystems.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/test_edge_cases.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/test_final_coverage.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/test_guard_approvals.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/test_guard_bootstrap.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/test_guard_capabilities.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/test_guard_claude_adapter.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/test_guard_cli.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/test_guard_codex_e2e.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/test_guard_codex_proxy.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/test_guard_config_paths.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/test_guard_connect_flow.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/test_guard_consumer_mode.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/test_guard_copilot_adapter.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/test_guard_copilot_proxy.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/test_guard_daemon_manager.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/test_guard_event_schema_v1.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/test_guard_events.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/test_guard_launch_env.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/test_guard_opencode_proxy.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/test_guard_product_flow.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/test_guard_protect.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/test_guard_render.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/test_guard_risk.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/test_guard_store_migrations.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/test_guard_surface_server.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/test_guard_verdicts.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/test_hermes_adapter.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/test_integration.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/test_lint_fixes.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/test_live_cisco_smoke.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/test_manifest.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/test_marketplace.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/test_mcp_security.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/test_operational_security.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/test_policy.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/test_quality_artifact.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/test_rule_registry.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/test_scanner.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/test_schema_contracts.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/test_security.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/test_security_ops.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/test_skill_security.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/test_submission.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/test_trust_scoring.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/test_trust_specs.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/test_verification.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/tests/test_versioning.py +0 -0
- {plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/uv.lock +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: plugin-scanner
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.79
|
|
4
4
|
Summary: Lint, verify, and gate plugin ecosystems for maintainers, CI, and publish workflows.
|
|
5
5
|
Project-URL: Homepage, https://github.com/hashgraph-online/ai-plugin-scanner
|
|
6
6
|
Project-URL: Repository, https://github.com/hashgraph-online/ai-plugin-scanner
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "plugin-scanner"
|
|
7
|
-
version = "2.0.
|
|
7
|
+
version = "2.0.79"
|
|
8
8
|
description = "Lint, verify, and gate plugin ecosystems for maintainers, CI, and publish workflows."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "Apache-2.0"
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "hol-guard"
|
|
7
|
-
version = "2.0.
|
|
7
|
+
version = "2.0.79"
|
|
8
8
|
description = "Protect local AI harnesses with HOL Guard and run scanner checks for Codex, Claude, Cursor, Gemini, and OpenCode."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "Apache-2.0"
|
{plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/guard/adapters/codex.py
RENAMED
|
@@ -5,6 +5,7 @@ from __future__ import annotations
|
|
|
5
5
|
import hashlib
|
|
6
6
|
import json
|
|
7
7
|
import os
|
|
8
|
+
import re
|
|
8
9
|
import shlex
|
|
9
10
|
import sys
|
|
10
11
|
from copy import deepcopy
|
|
@@ -43,11 +44,13 @@ def _read_toml(path: Path) -> dict[str, object]:
|
|
|
43
44
|
_MANAGED_HOOK_STATUS_MESSAGE = "HOL Guard checking tool action"
|
|
44
45
|
_MANAGED_PROMPT_HOOK_STATUS_MESSAGE = "HOL Guard checking prompt"
|
|
45
46
|
_MANAGED_PERMISSION_HOOK_STATUS_MESSAGE = "HOL Guard checking Codex approval request"
|
|
47
|
+
_MANAGED_POST_TOOL_HOOK_STATUS_MESSAGE = "HOL Guard checking tool result"
|
|
46
48
|
_LEGACY_MANAGED_HOOK_STATUS_MESSAGES = {
|
|
47
49
|
"HOL Guard checking Bash command",
|
|
48
50
|
_MANAGED_HOOK_STATUS_MESSAGE,
|
|
49
51
|
_MANAGED_PROMPT_HOOK_STATUS_MESSAGE,
|
|
50
52
|
_MANAGED_PERMISSION_HOOK_STATUS_MESSAGE,
|
|
53
|
+
_MANAGED_POST_TOOL_HOOK_STATUS_MESSAGE,
|
|
51
54
|
}
|
|
52
55
|
|
|
53
56
|
|
|
@@ -113,7 +116,7 @@ def _pre_tool_hook_group(context: HarnessContext) -> dict[str, object]:
|
|
|
113
116
|
{
|
|
114
117
|
"type": "command",
|
|
115
118
|
"command": _hook_command(context),
|
|
116
|
-
"
|
|
119
|
+
"timeout": 30,
|
|
117
120
|
"statusMessage": _MANAGED_HOOK_STATUS_MESSAGE,
|
|
118
121
|
}
|
|
119
122
|
],
|
|
@@ -126,7 +129,7 @@ def _prompt_hook_group(context: HarnessContext) -> dict[str, object]:
|
|
|
126
129
|
{
|
|
127
130
|
"type": "command",
|
|
128
131
|
"command": _hook_command(context),
|
|
129
|
-
"
|
|
132
|
+
"timeout": 30,
|
|
130
133
|
"statusMessage": _MANAGED_PROMPT_HOOK_STATUS_MESSAGE,
|
|
131
134
|
}
|
|
132
135
|
],
|
|
@@ -140,18 +143,33 @@ def _permission_request_hook_group(context: HarnessContext) -> dict[str, object]
|
|
|
140
143
|
{
|
|
141
144
|
"type": "command",
|
|
142
145
|
"command": _hook_command(context),
|
|
143
|
-
"
|
|
146
|
+
"timeout": 30,
|
|
144
147
|
"statusMessage": _MANAGED_PERMISSION_HOOK_STATUS_MESSAGE,
|
|
145
148
|
}
|
|
146
149
|
],
|
|
147
150
|
}
|
|
148
151
|
|
|
149
152
|
|
|
153
|
+
def _post_tool_hook_group(context: HarnessContext) -> dict[str, object]:
|
|
154
|
+
return {
|
|
155
|
+
"matcher": "Bash",
|
|
156
|
+
"hooks": [
|
|
157
|
+
{
|
|
158
|
+
"type": "command",
|
|
159
|
+
"command": _hook_command(context),
|
|
160
|
+
"timeout": 30,
|
|
161
|
+
"statusMessage": _MANAGED_POST_TOOL_HOOK_STATUS_MESSAGE,
|
|
162
|
+
}
|
|
163
|
+
],
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
|
|
150
167
|
def _managed_hook_groups(context: HarnessContext) -> dict[str, dict[str, object]]:
|
|
151
168
|
return {
|
|
152
169
|
"PreToolUse": _pre_tool_hook_group(context),
|
|
153
170
|
"PermissionRequest": _permission_request_hook_group(context),
|
|
154
171
|
"UserPromptSubmit": _prompt_hook_group(context),
|
|
172
|
+
"PostToolUse": _post_tool_hook_group(context),
|
|
155
173
|
}
|
|
156
174
|
|
|
157
175
|
|
|
@@ -179,14 +197,13 @@ def _is_managed_hook_command(command: object) -> bool:
|
|
|
179
197
|
if tokens[1] != "-c":
|
|
180
198
|
return False
|
|
181
199
|
code = tokens[2]
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
and "
|
|
185
|
-
and "'
|
|
186
|
-
and "'
|
|
187
|
-
and "'--harness'" in code
|
|
188
|
-
and "'codex'" in code
|
|
200
|
+
has_guard_call = (
|
|
201
|
+
re.search(r"['\"]guard['\"]", code) is not None
|
|
202
|
+
and re.search(r"['\"]hook['\"]", code) is not None
|
|
203
|
+
and re.search(r"['\"]--harness['\"]", code) is not None
|
|
204
|
+
and re.search(r"['\"]codex['\"]", code) is not None
|
|
189
205
|
)
|
|
206
|
+
return "codex_plugin_scanner.cli" in code and "main([" in code and has_guard_call
|
|
190
207
|
|
|
191
208
|
|
|
192
209
|
def _argv_targets_codex(argv: list[str]) -> bool:
|
|
@@ -249,7 +266,7 @@ def _remove_hook_groups(groups: object) -> list[object]:
|
|
|
249
266
|
def _remove_managed_hook_events(hooks: dict[str, object]) -> tuple[dict[str, object], bool]:
|
|
250
267
|
updated_hooks = dict(hooks)
|
|
251
268
|
changed = False
|
|
252
|
-
for event_name in ("PreToolUse", "PermissionRequest", "UserPromptSubmit"):
|
|
269
|
+
for event_name in ("PreToolUse", "PermissionRequest", "UserPromptSubmit", "PostToolUse"):
|
|
253
270
|
original_groups = deepcopy(updated_hooks.get(event_name))
|
|
254
271
|
remaining = _remove_hook_groups(original_groups)
|
|
255
272
|
managed_removed = isinstance(original_groups, list) and remaining != original_groups
|
|
@@ -273,6 +290,7 @@ def codex_native_hook_state(context: HarnessContext) -> dict[str, object]:
|
|
|
273
290
|
pre_tool_groups = hooks.get("PreToolUse") if isinstance(hooks, dict) else None
|
|
274
291
|
permission_groups = hooks.get("PermissionRequest") if isinstance(hooks, dict) else None
|
|
275
292
|
prompt_groups = hooks.get("UserPromptSubmit") if isinstance(hooks, dict) else None
|
|
293
|
+
post_tool_groups = hooks.get("PostToolUse") if isinstance(hooks, dict) else None
|
|
276
294
|
pre_tool_hook_installed = isinstance(pre_tool_groups, list) and any(
|
|
277
295
|
_is_managed_hook_group(group) for group in pre_tool_groups
|
|
278
296
|
)
|
|
@@ -282,7 +300,12 @@ def codex_native_hook_state(context: HarnessContext) -> dict[str, object]:
|
|
|
282
300
|
prompt_hook_installed = isinstance(prompt_groups, list) and any(
|
|
283
301
|
_is_managed_hook_group(group) for group in prompt_groups
|
|
284
302
|
)
|
|
285
|
-
|
|
303
|
+
post_tool_hook_installed = isinstance(post_tool_groups, list) and any(
|
|
304
|
+
_is_managed_hook_group(group) for group in post_tool_groups
|
|
305
|
+
)
|
|
306
|
+
managed_hook_installed = (
|
|
307
|
+
pre_tool_hook_installed and permission_hook_installed and prompt_hook_installed and post_tool_hook_installed
|
|
308
|
+
)
|
|
286
309
|
return {
|
|
287
310
|
"config_path": str(config_path),
|
|
288
311
|
"config_present": config_path.is_file(),
|
|
@@ -292,6 +315,7 @@ def codex_native_hook_state(context: HarnessContext) -> dict[str, object]:
|
|
|
292
315
|
"managed_pre_tool_hook_installed": pre_tool_hook_installed,
|
|
293
316
|
"managed_permission_request_hook_installed": permission_hook_installed,
|
|
294
317
|
"managed_prompt_hook_installed": prompt_hook_installed,
|
|
318
|
+
"managed_post_tool_hook_installed": post_tool_hook_installed,
|
|
295
319
|
"managed_hook_installed": managed_hook_installed,
|
|
296
320
|
"protection_active": isinstance(features, dict)
|
|
297
321
|
and features.get("codex_hooks") is True
|
{plugin_scanner-2.0.78 → plugin_scanner-2.0.79}/src/codex_plugin_scanner/guard/cli/commands.py
RENAMED
|
@@ -1419,11 +1419,13 @@ def run_guard_command(
|
|
|
1419
1419
|
artifact_id = runtime_artifact.artifact_id
|
|
1420
1420
|
artifact_name = runtime_artifact.name
|
|
1421
1421
|
policy_harness = _canonical_harness_name(args.harness)
|
|
1422
|
-
stored_policy_action =
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1422
|
+
stored_policy_action = _runtime_stored_policy_action(
|
|
1423
|
+
store=store,
|
|
1424
|
+
harness=policy_harness,
|
|
1425
|
+
artifact=runtime_artifact,
|
|
1426
|
+
artifact_id=artifact_id,
|
|
1427
|
+
artifact_hash=runtime_artifact_hash,
|
|
1428
|
+
workspace=str(runtime_workspace) if runtime_workspace else None,
|
|
1427
1429
|
)
|
|
1428
1430
|
if stored_policy_action is None:
|
|
1429
1431
|
legacy_artifact = _legacy_claude_alias_runtime_artifact(
|
|
@@ -1433,11 +1435,13 @@ def run_guard_command(
|
|
|
1433
1435
|
workspace=runtime_workspace,
|
|
1434
1436
|
)
|
|
1435
1437
|
if legacy_artifact is not None:
|
|
1436
|
-
stored_policy_action =
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1438
|
+
stored_policy_action = _runtime_stored_policy_action(
|
|
1439
|
+
store=store,
|
|
1440
|
+
harness=args.harness,
|
|
1441
|
+
artifact=legacy_artifact,
|
|
1442
|
+
artifact_id=legacy_artifact.artifact_id,
|
|
1443
|
+
artifact_hash=artifact_hash(legacy_artifact),
|
|
1444
|
+
workspace=str(runtime_workspace) if runtime_workspace else None,
|
|
1441
1445
|
)
|
|
1442
1446
|
policy_action = _coalesce_string(
|
|
1443
1447
|
getattr(args, "policy_action", None),
|
|
@@ -2771,6 +2775,37 @@ def _native_approval_center_context(response_payload: dict[str, object], *, harn
|
|
|
2771
2775
|
)
|
|
2772
2776
|
|
|
2773
2777
|
|
|
2778
|
+
def _runtime_stored_policy_action(
|
|
2779
|
+
*,
|
|
2780
|
+
store: GuardStore,
|
|
2781
|
+
harness: str,
|
|
2782
|
+
artifact: GuardArtifact,
|
|
2783
|
+
artifact_id: str,
|
|
2784
|
+
artifact_hash: str,
|
|
2785
|
+
workspace: str | None,
|
|
2786
|
+
) -> str | None:
|
|
2787
|
+
decision = store.resolve_policy_decision(
|
|
2788
|
+
harness,
|
|
2789
|
+
artifact_id,
|
|
2790
|
+
artifact_hash,
|
|
2791
|
+
workspace,
|
|
2792
|
+
artifact.publisher,
|
|
2793
|
+
)
|
|
2794
|
+
if decision is None:
|
|
2795
|
+
return None
|
|
2796
|
+
action = _optional_string(decision.get("action"))
|
|
2797
|
+
if action is None:
|
|
2798
|
+
return None
|
|
2799
|
+
scope = _optional_string(decision.get("scope"))
|
|
2800
|
+
if (
|
|
2801
|
+
action in {"allow", "warn", "review"}
|
|
2802
|
+
and scope in {"workspace", "publisher", "harness", "global"}
|
|
2803
|
+
and _runtime_artifact_risk_classes(artifact)
|
|
2804
|
+
):
|
|
2805
|
+
return None
|
|
2806
|
+
return action
|
|
2807
|
+
|
|
2808
|
+
|
|
2774
2809
|
def _runtime_artifact_policy_action(config: GuardConfig, artifact: GuardArtifact, harness: str) -> str:
|
|
2775
2810
|
if _prompt_requires_hard_block(artifact):
|
|
2776
2811
|
return "block"
|
|
@@ -2933,6 +2968,13 @@ def _runtime_artifact_native_reason(artifact: GuardArtifact, response_payload: d
|
|
|
2933
2968
|
harness = response_payload.get("harness")
|
|
2934
2969
|
prompt_classes = _prompt_request_classes(artifact)
|
|
2935
2970
|
if harness == "codex" and "secret_read" in prompt_classes:
|
|
2971
|
+
prompt_summary = artifact.metadata.get("prompt_summary")
|
|
2972
|
+
if isinstance(prompt_summary, str) and "credential-looking local file" in prompt_summary:
|
|
2973
|
+
return (
|
|
2974
|
+
"HOL Guard stopped this Codex prompt before Codex could open a credential-looking local file. "
|
|
2975
|
+
"Codex does not expose native approval prompts for Read-tool file reads, so Guard blocks this "
|
|
2976
|
+
"request at prompt time."
|
|
2977
|
+
)
|
|
2936
2978
|
return (
|
|
2937
2979
|
"HOL Guard stopped this Codex prompt before Codex could open a sensitive local file. Codex does not "
|
|
2938
2980
|
"expose native approval prompts for Read-tool file reads, so Guard blocks this request at prompt time."
|
|
@@ -3164,6 +3206,13 @@ def _emit_native_hook_response(
|
|
|
3164
3206
|
if payload:
|
|
3165
3207
|
_write_json_line(payload, output_stream=output_stream)
|
|
3166
3208
|
return
|
|
3209
|
+
if event_name == "PostToolUse" and policy_action in {"block", "sandbox-required", "require-reapproval"}:
|
|
3210
|
+
payload["decision"] = "block"
|
|
3211
|
+
payload["reason"] = reason
|
|
3212
|
+
payload["continue"] = False
|
|
3213
|
+
payload["stopReason"] = reason
|
|
3214
|
+
_write_json_line(payload, output_stream=output_stream)
|
|
3215
|
+
return
|
|
3167
3216
|
permission_decision = _native_hook_permission_decision(policy_action, harness=harness)
|
|
3168
3217
|
if harness == "codex" and event_name == "PreToolUse" and permission_decision is None:
|
|
3169
3218
|
return
|
|
@@ -3594,6 +3643,14 @@ def _hook_runtime_artifact(
|
|
|
3594
3643
|
) -> GuardArtifact | None:
|
|
3595
3644
|
harness = _canonical_harness_name(harness)
|
|
3596
3645
|
event_name = _hook_event_name(payload)
|
|
3646
|
+
if harness == "codex" and event_name == "PostToolUse":
|
|
3647
|
+
output_artifact = _codex_post_tool_output_artifact(
|
|
3648
|
+
payload=payload,
|
|
3649
|
+
config_path=str(_runtime_policy_path(harness, home_dir, workspace)),
|
|
3650
|
+
source_scope=_coalesce_string(payload.get("source_scope"), "project"),
|
|
3651
|
+
)
|
|
3652
|
+
if output_artifact is not None:
|
|
3653
|
+
return output_artifact
|
|
3597
3654
|
if event_name == "UserPromptSubmit":
|
|
3598
3655
|
prompt_text = payload.get("prompt")
|
|
3599
3656
|
if isinstance(prompt_text, str) and prompt_text.strip():
|
|
@@ -3619,6 +3676,13 @@ def _hook_runtime_artifact(
|
|
|
3619
3676
|
)
|
|
3620
3677
|
if prompt_artifacts:
|
|
3621
3678
|
return _merged_prompt_runtime_artifact(harness, prompt_artifacts)
|
|
3679
|
+
prompt_file_artifact = _codex_prompt_credential_file_artifact(
|
|
3680
|
+
prompt_text=prompt_text,
|
|
3681
|
+
cwd=workspace,
|
|
3682
|
+
config_path=config_path,
|
|
3683
|
+
)
|
|
3684
|
+
if prompt_file_artifact is not None:
|
|
3685
|
+
return prompt_file_artifact
|
|
3622
3686
|
request = extract_sensitive_file_read_request(
|
|
3623
3687
|
payload.get("tool_name"),
|
|
3624
3688
|
payload.get("tool_input", payload.get("arguments")),
|
|
@@ -3650,6 +3714,180 @@ def _hook_runtime_artifact(
|
|
|
3650
3714
|
)
|
|
3651
3715
|
|
|
3652
3716
|
|
|
3717
|
+
_CODEX_SECRET_OUTPUT_PATTERN = re.compile(
|
|
3718
|
+
r"(?i)(?:fake[_-]?credential|fake[_-]?secret|"
|
|
3719
|
+
r"(?:api[_-]?key|auth[_-]?token|credential|npm[_-]?token|private[_-]?key|secret|token|password)\s*[:=])"
|
|
3720
|
+
)
|
|
3721
|
+
_CODEX_TOOL_RESPONSE_MAX_DEPTH = 5
|
|
3722
|
+
_CODEX_TOOL_RESPONSE_TEXT_LIMIT = 20000
|
|
3723
|
+
_CODEX_PROMPT_FILE_FINGERPRINT_LENGTH = 24
|
|
3724
|
+
|
|
3725
|
+
|
|
3726
|
+
def _codex_post_tool_output_artifact(
|
|
3727
|
+
*,
|
|
3728
|
+
payload: dict[str, object],
|
|
3729
|
+
config_path: str,
|
|
3730
|
+
source_scope: str,
|
|
3731
|
+
) -> GuardArtifact | None:
|
|
3732
|
+
response_text = _collect_codex_tool_response_text(payload.get("tool_response"))
|
|
3733
|
+
if not response_text or _CODEX_SECRET_OUTPUT_PATTERN.search(response_text) is None:
|
|
3734
|
+
return None
|
|
3735
|
+
tool_name = _coalesce_string(payload.get("tool_name"), "Bash")
|
|
3736
|
+
tool_input = payload.get("tool_input")
|
|
3737
|
+
command_text = ""
|
|
3738
|
+
if isinstance(tool_input, dict):
|
|
3739
|
+
command = tool_input.get("command")
|
|
3740
|
+
if isinstance(command, str):
|
|
3741
|
+
command_text = command.strip()
|
|
3742
|
+
if not command_text:
|
|
3743
|
+
command_text = tool_name
|
|
3744
|
+
fingerprint = hashlib.sha256(
|
|
3745
|
+
json.dumps(
|
|
3746
|
+
{
|
|
3747
|
+
"tool_name": tool_name,
|
|
3748
|
+
"command_text": command_text,
|
|
3749
|
+
"output_class": "credential-looking output",
|
|
3750
|
+
},
|
|
3751
|
+
sort_keys=True,
|
|
3752
|
+
).encode("utf-8")
|
|
3753
|
+
).hexdigest()
|
|
3754
|
+
return GuardArtifact(
|
|
3755
|
+
artifact_id=f"codex:{source_scope}:tool-output:{fingerprint}",
|
|
3756
|
+
name=f"{tool_name} credential-looking output",
|
|
3757
|
+
harness="codex",
|
|
3758
|
+
artifact_type="tool_action_request",
|
|
3759
|
+
source_scope=source_scope,
|
|
3760
|
+
config_path=config_path,
|
|
3761
|
+
metadata={
|
|
3762
|
+
"tool_name": tool_name,
|
|
3763
|
+
"command_text": command_text,
|
|
3764
|
+
"action_class": "credential exfiltration shell command",
|
|
3765
|
+
"request_summary": (
|
|
3766
|
+
f"Codex tool `{tool_name}` produced credential-looking output while running `{command_text}`."
|
|
3767
|
+
),
|
|
3768
|
+
"runtime_request_signals": ["tool output contains credential-looking material"],
|
|
3769
|
+
"runtime_request_summary": (
|
|
3770
|
+
"Requests a sensitive native tool action: credential-looking output reached Codex."
|
|
3771
|
+
),
|
|
3772
|
+
"runtime_request_reason": (
|
|
3773
|
+
"Guard inspects supported Codex tool output before Codex uses it, so accidental secret reads can be "
|
|
3774
|
+
"stopped even when the filename was not obviously sensitive."
|
|
3775
|
+
),
|
|
3776
|
+
},
|
|
3777
|
+
)
|
|
3778
|
+
|
|
3779
|
+
|
|
3780
|
+
def _collect_codex_tool_response_text(value: object, *, depth: int = 0) -> str:
|
|
3781
|
+
if depth > _CODEX_TOOL_RESPONSE_MAX_DEPTH:
|
|
3782
|
+
return ""
|
|
3783
|
+
if isinstance(value, str):
|
|
3784
|
+
return value[:_CODEX_TOOL_RESPONSE_TEXT_LIMIT]
|
|
3785
|
+
if isinstance(value, dict):
|
|
3786
|
+
parts: list[str] = []
|
|
3787
|
+
for key, child in value.items():
|
|
3788
|
+
key_text = str(key).lower()
|
|
3789
|
+
if key_text in {"stdout", "stderr", "output", "text", "content", "result", "message"} or depth > 0:
|
|
3790
|
+
text = _collect_codex_tool_response_text(child, depth=depth + 1)
|
|
3791
|
+
if text:
|
|
3792
|
+
parts.append(text)
|
|
3793
|
+
return "\n".join(parts)[:_CODEX_TOOL_RESPONSE_TEXT_LIMIT]
|
|
3794
|
+
if isinstance(value, list):
|
|
3795
|
+
return "\n".join(_collect_codex_tool_response_text(item, depth=depth + 1) for item in value)[
|
|
3796
|
+
:_CODEX_TOOL_RESPONSE_TEXT_LIMIT
|
|
3797
|
+
]
|
|
3798
|
+
return ""
|
|
3799
|
+
|
|
3800
|
+
|
|
3801
|
+
_PROMPT_PATH_TOKEN_PATTERN = re.compile(
|
|
3802
|
+
r"(?<![\w/.-])\.[A-Za-z0-9][A-Za-z0-9_.-]{0,255}|"
|
|
3803
|
+
r"(?:~|\.{1,2}|/)[^\s'\"`<>|;(){}\[\]]{0,255}"
|
|
3804
|
+
)
|
|
3805
|
+
_PROMPT_FILE_READ_VERB_PATTERN = re.compile(r"\b(?:read|open|print|show|dump|cat|head|tail|less|view|display)\b", re.I)
|
|
3806
|
+
_PROMPT_CONTENT_SCAN_MAX_BYTES = 64 * 1024
|
|
3807
|
+
_PROMPT_CONTENT_SCAN_SKIP_BASENAMES = frozenset(
|
|
3808
|
+
{
|
|
3809
|
+
".env",
|
|
3810
|
+
".npmrc",
|
|
3811
|
+
".pypirc",
|
|
3812
|
+
".netrc",
|
|
3813
|
+
".git-credentials",
|
|
3814
|
+
}
|
|
3815
|
+
)
|
|
3816
|
+
|
|
3817
|
+
|
|
3818
|
+
def _codex_prompt_credential_file_artifact(
|
|
3819
|
+
*,
|
|
3820
|
+
prompt_text: str,
|
|
3821
|
+
cwd: Path | None,
|
|
3822
|
+
config_path: str,
|
|
3823
|
+
) -> GuardArtifact | None:
|
|
3824
|
+
if _PROMPT_FILE_READ_VERB_PATTERN.search(prompt_text) is None:
|
|
3825
|
+
return None
|
|
3826
|
+
for match in _PROMPT_PATH_TOKEN_PATTERN.finditer(prompt_text):
|
|
3827
|
+
requested_path = match.group(0)
|
|
3828
|
+
path = _resolve_prompt_scan_path(requested_path, cwd=cwd)
|
|
3829
|
+
if path is None or path.name in _PROMPT_CONTENT_SCAN_SKIP_BASENAMES:
|
|
3830
|
+
continue
|
|
3831
|
+
if not path.name.startswith("."):
|
|
3832
|
+
continue
|
|
3833
|
+
if not path.is_file():
|
|
3834
|
+
continue
|
|
3835
|
+
try:
|
|
3836
|
+
with path.open("rb") as handle:
|
|
3837
|
+
content = handle.read(_PROMPT_CONTENT_SCAN_MAX_BYTES).decode("utf-8", errors="ignore")
|
|
3838
|
+
except OSError:
|
|
3839
|
+
continue
|
|
3840
|
+
if _CODEX_SECRET_OUTPUT_PATTERN.search(content) is None:
|
|
3841
|
+
continue
|
|
3842
|
+
normalized_path = str(path)
|
|
3843
|
+
fingerprint = hashlib.sha256(
|
|
3844
|
+
json.dumps(
|
|
3845
|
+
{
|
|
3846
|
+
"harness": "codex",
|
|
3847
|
+
"prompt_path": normalized_path,
|
|
3848
|
+
"content_class": "credential-looking local file",
|
|
3849
|
+
},
|
|
3850
|
+
sort_keys=True,
|
|
3851
|
+
).encode("utf-8")
|
|
3852
|
+
).hexdigest()[:_CODEX_PROMPT_FILE_FINGERPRINT_LENGTH]
|
|
3853
|
+
return GuardArtifact(
|
|
3854
|
+
artifact_id=f"codex:project:prompt-file:{fingerprint}",
|
|
3855
|
+
name=f"credential-looking local file {path.name}",
|
|
3856
|
+
harness="codex",
|
|
3857
|
+
artifact_type="prompt_request",
|
|
3858
|
+
source_scope="project",
|
|
3859
|
+
config_path=config_path,
|
|
3860
|
+
metadata={
|
|
3861
|
+
"prompt_signals": ["requested file content contains credential-looking material"],
|
|
3862
|
+
"prompt_summary": "Prompt asks Codex to read a credential-looking local file.",
|
|
3863
|
+
"prompt_matched_text": requested_path,
|
|
3864
|
+
"prompt_request_class": "secret_read",
|
|
3865
|
+
"prompt_request_classes": ["secret_read"],
|
|
3866
|
+
"runtime_request_summary": "Prompt requests direct access to a credential-looking local file.",
|
|
3867
|
+
"runtime_request_reason": (
|
|
3868
|
+
"Guard scanned a small local dotfile before Codex read it and found credential-looking text."
|
|
3869
|
+
),
|
|
3870
|
+
"normalized_path": normalized_path,
|
|
3871
|
+
},
|
|
3872
|
+
)
|
|
3873
|
+
return None
|
|
3874
|
+
|
|
3875
|
+
|
|
3876
|
+
def _resolve_prompt_scan_path(requested_path: str, *, cwd: Path | None) -> Path | None:
|
|
3877
|
+
stripped = requested_path.strip().strip("'\"").rstrip(".,;:!?)]}")
|
|
3878
|
+
if not stripped:
|
|
3879
|
+
return None
|
|
3880
|
+
try:
|
|
3881
|
+
expanded = Path(stripped).expanduser()
|
|
3882
|
+
except RuntimeError:
|
|
3883
|
+
return None
|
|
3884
|
+
if not expanded.is_absolute():
|
|
3885
|
+
expanded = (cwd or Path.cwd()) / expanded
|
|
3886
|
+
with suppress(OSError):
|
|
3887
|
+
return expanded.resolve(strict=False)
|
|
3888
|
+
return expanded
|
|
3889
|
+
|
|
3890
|
+
|
|
3653
3891
|
def _legacy_claude_alias_runtime_artifact(
|
|
3654
3892
|
*,
|
|
3655
3893
|
artifact: GuardArtifact,
|
|
@@ -1170,11 +1170,31 @@ class GuardStore:
|
|
|
1170
1170
|
publisher: str | None = None,
|
|
1171
1171
|
now: str | None = None,
|
|
1172
1172
|
) -> str | None:
|
|
1173
|
+
decision = self.resolve_policy_decision(
|
|
1174
|
+
harness,
|
|
1175
|
+
artifact_id,
|
|
1176
|
+
artifact_hash,
|
|
1177
|
+
workspace,
|
|
1178
|
+
publisher,
|
|
1179
|
+
now,
|
|
1180
|
+
)
|
|
1181
|
+
return str(decision["action"]) if decision is not None else None
|
|
1182
|
+
|
|
1183
|
+
def resolve_policy_decision(
|
|
1184
|
+
self,
|
|
1185
|
+
harness: str,
|
|
1186
|
+
artifact_id: str | None,
|
|
1187
|
+
artifact_hash: str | None = None,
|
|
1188
|
+
workspace: str | None = None,
|
|
1189
|
+
publisher: str | None = None,
|
|
1190
|
+
now: str | None = None,
|
|
1191
|
+
) -> dict[str, str | None] | None:
|
|
1173
1192
|
current_time = now or _now()
|
|
1174
1193
|
with self._connect() as connection:
|
|
1175
1194
|
rows = connection.execute(
|
|
1176
1195
|
"""
|
|
1177
|
-
select scope, action, artifact_hash
|
|
1196
|
+
select harness, scope, artifact_id, action, artifact_hash, workspace, publisher, source
|
|
1197
|
+
from policy_decisions
|
|
1178
1198
|
where (harness = ? or harness = '*') and (
|
|
1179
1199
|
(
|
|
1180
1200
|
scope = 'artifact' and artifact_id = ? and (
|
|
@@ -1193,7 +1213,19 @@ class GuardStore:
|
|
|
1193
1213
|
""",
|
|
1194
1214
|
(harness, artifact_id, artifact_hash, artifact_hash, workspace, publisher, current_time),
|
|
1195
1215
|
).fetchall()
|
|
1196
|
-
|
|
1216
|
+
if not rows:
|
|
1217
|
+
return None
|
|
1218
|
+
row = rows[0]
|
|
1219
|
+
return {
|
|
1220
|
+
"harness": row["harness"],
|
|
1221
|
+
"scope": row["scope"],
|
|
1222
|
+
"artifact_id": row["artifact_id"],
|
|
1223
|
+
"artifact_hash": row["artifact_hash"],
|
|
1224
|
+
"workspace": row["workspace"],
|
|
1225
|
+
"publisher": row["publisher"],
|
|
1226
|
+
"action": row["action"],
|
|
1227
|
+
"source": row["source"],
|
|
1228
|
+
}
|
|
1197
1229
|
|
|
1198
1230
|
@staticmethod
|
|
1199
1231
|
def _normalized_policy_keys(decision: PolicyDecision) -> tuple[str | None, str | None, str | None, str | None]:
|
|
@@ -2,7 +2,9 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import json
|
|
4
4
|
import os
|
|
5
|
+
import shlex
|
|
5
6
|
import subprocess
|
|
7
|
+
import sys
|
|
6
8
|
from pathlib import Path
|
|
7
9
|
|
|
8
10
|
try:
|
|
@@ -271,6 +273,85 @@ def test_guard_install_codex_migrates_legacy_bash_only_managed_hook(tmp_path, ca
|
|
|
271
273
|
assert len(hooks_payload["hooks"]["UserPromptSubmit"]) == 1
|
|
272
274
|
|
|
273
275
|
|
|
276
|
+
def test_guard_install_codex_migrates_stale_python_c_managed_hooks(tmp_path, capsys):
|
|
277
|
+
home_dir = tmp_path / "home"
|
|
278
|
+
workspace_dir = tmp_path / "workspace"
|
|
279
|
+
stale_worktree = tmp_path / "deleted-worktree"
|
|
280
|
+
_write_text(workspace_dir / ".codex" / "config.toml", 'approval_policy = "never"\n')
|
|
281
|
+
stale_command = (
|
|
282
|
+
f"{sys.executable} -c "
|
|
283
|
+
+ shlex.quote(
|
|
284
|
+
"import sys;"
|
|
285
|
+
f"sys.path[:0]=[{str(stale_worktree / 'src')!r}];"
|
|
286
|
+
"from codex_plugin_scanner.cli import main;"
|
|
287
|
+
"raise SystemExit(main(["
|
|
288
|
+
'"guard", "hook", "--guard-home", '
|
|
289
|
+
f"{str(home_dir / '.hol-guard')!r}, "
|
|
290
|
+
'"--harness", "codex"'
|
|
291
|
+
"]))"
|
|
292
|
+
)
|
|
293
|
+
)
|
|
294
|
+
_write_text(
|
|
295
|
+
workspace_dir / ".codex" / "hooks.json",
|
|
296
|
+
json.dumps(
|
|
297
|
+
{
|
|
298
|
+
"hooks": {
|
|
299
|
+
"PreToolUse": [
|
|
300
|
+
{
|
|
301
|
+
"matcher": "Bash",
|
|
302
|
+
"hooks": [
|
|
303
|
+
{
|
|
304
|
+
"type": "command",
|
|
305
|
+
"command": stale_command,
|
|
306
|
+
"timeoutSec": 30,
|
|
307
|
+
"statusMessage": "HOL Guard checking tool action",
|
|
308
|
+
}
|
|
309
|
+
],
|
|
310
|
+
}
|
|
311
|
+
],
|
|
312
|
+
"UserPromptSubmit": [
|
|
313
|
+
{
|
|
314
|
+
"hooks": [
|
|
315
|
+
{
|
|
316
|
+
"type": "command",
|
|
317
|
+
"command": stale_command,
|
|
318
|
+
"timeoutSec": 30,
|
|
319
|
+
"statusMessage": "HOL Guard checking prompt",
|
|
320
|
+
}
|
|
321
|
+
]
|
|
322
|
+
}
|
|
323
|
+
],
|
|
324
|
+
}
|
|
325
|
+
},
|
|
326
|
+
indent=2,
|
|
327
|
+
)
|
|
328
|
+
+ "\n",
|
|
329
|
+
)
|
|
330
|
+
|
|
331
|
+
install_rc = main(
|
|
332
|
+
[
|
|
333
|
+
"guard",
|
|
334
|
+
"install",
|
|
335
|
+
"codex",
|
|
336
|
+
"--home",
|
|
337
|
+
str(home_dir),
|
|
338
|
+
"--workspace",
|
|
339
|
+
str(workspace_dir),
|
|
340
|
+
"--json",
|
|
341
|
+
]
|
|
342
|
+
)
|
|
343
|
+
json.loads(capsys.readouterr().out)
|
|
344
|
+
hooks_payload = json.loads((workspace_dir / ".codex" / "hooks.json").read_text(encoding="utf-8"))
|
|
345
|
+
|
|
346
|
+
assert install_rc == 0
|
|
347
|
+
assert len(hooks_payload["hooks"]["PreToolUse"]) == 1
|
|
348
|
+
assert len(hooks_payload["hooks"]["UserPromptSubmit"]) == 1
|
|
349
|
+
assert len(hooks_payload["hooks"]["PermissionRequest"]) == 1
|
|
350
|
+
assert len(hooks_payload["hooks"]["PostToolUse"]) == 1
|
|
351
|
+
all_commands = json.dumps(hooks_payload)
|
|
352
|
+
assert str(stale_worktree) not in all_commands
|
|
353
|
+
|
|
354
|
+
|
|
274
355
|
def test_guard_install_codex_workspace_cleans_stale_global_managed_hook(tmp_path, capsys):
|
|
275
356
|
home_dir = tmp_path / "home"
|
|
276
357
|
workspace_dir = tmp_path / "workspace"
|