plugin-scanner 2.0.104__tar.gz → 2.0.106__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.104 → plugin_scanner-2.0.106}/PKG-INFO +1 -1
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/pyproject.toml +1 -1
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/pyproject.toml.bak +1 -1
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/cli/commands.py +11 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/cli/render.py +5 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/config.py +2 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/risk.py +2 -6
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/runtime/actions.py +32 -2
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/runtime/detectors.py +43 -1
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/runtime/runner.py +53 -3
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/runtime/secret_file_requests.py +3 -45
- plugin_scanner-2.0.106/src/codex_plugin_scanner/guard/runtime/secret_sensitivity.py +217 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/version.py +1 -1
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_guard_cli.py +40 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_guard_config_paths.py +2 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_guard_risk.py +60 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_guard_runtime.py +46 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_guard_runtime_actions.py +45 -1
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_guard_runtime_detectors.py +292 -3
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/.clusterfuzzlite/Dockerfile +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/.clusterfuzzlite/build.sh +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/.clusterfuzzlite/project.yaml +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/.clusterfuzzlite/requirements-atheris.txt +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/.dockerignore +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/.github/CODEOWNERS +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/.github/ISSUE_TEMPLATE/bug-report.yml +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/.github/ISSUE_TEMPLATE/feature-request.yml +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/.github/dependabot.yml +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/.github/workflows/ci.yml +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/.github/workflows/codeql.yml +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/.github/workflows/dependabot-uv-lock.yml +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/.github/workflows/fuzz.yml +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/.github/workflows/harness-smoke.yml +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/.github/workflows/publish.yml +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/.github/workflows/scorecard.yml +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/.gitignore +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/.pre-commit-hooks.yaml +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/CONTRIBUTING.md +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/Dockerfile +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/LICENSE +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/README.md +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/SECURITY.md +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/dashboard/index.html +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/dashboard/package.json +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/dashboard/pnpm-lock.yaml +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/dashboard/public/apple-touch-icon.png +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/dashboard/public/brand/Logo_Icon_Dark.png +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/dashboard/public/brand/Logo_Whole.png +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/dashboard/public/favicon-16x16.png +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/dashboard/public/favicon-32x32.png +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/dashboard/public/favicon.ico +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/dashboard/src/app.tsx +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/dashboard/src/approval-center-layout.tsx +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/dashboard/src/approval-center-primitives.tsx +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/dashboard/src/approval-center-utils.ts +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/dashboard/src/fleet-workspace.tsx +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/dashboard/src/guard-api.test.ts +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/dashboard/src/guard-api.ts +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/dashboard/src/guard-demo.ts +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/dashboard/src/guard-types.ts +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/dashboard/src/main.tsx +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/dashboard/src/receipts-workspace.tsx +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/dashboard/src/runtime-overview.tsx +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/dashboard/src/settings-workspace.tsx +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/dashboard/src/styles.css +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/dashboard/src/vite-env.d.ts +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/dashboard/tsconfig.json +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/dashboard/vite.config.ts +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/docker-requirements.txt +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/docs/guard/approval-audit.md +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/docs/guard/architecture.md +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/docs/guard/get-started.md +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/docs/guard/harness-support.md +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/docs/guard/local-vs-cloud.md +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/docs/guard/testing-matrix.md +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/docs/trust/mcp-trust-draft.md +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/docs/trust/plugin-trust-draft.md +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/docs/trust/skill-trust-local.md +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/fuzzers/manifest_fuzzer.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/requirements.txt +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/schemas/plugin-quality.v1.json +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/schemas/scan-result.v1.json +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/schemas/verify-result.v1.json +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/__init__.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/action_runner.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/argparse_utils.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/checks/__init__.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/checks/best_practices.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/checks/claude.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/checks/code_quality.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/checks/ecosystem_common.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/checks/gemini.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/checks/manifest.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/checks/manifest_support.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/checks/marketplace.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/checks/mcp_security.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/checks/opencode.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/checks/operational_security.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/checks/security.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/checks/skill_security.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/cli.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/cli_ui.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/config.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/ecosystems/__init__.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/ecosystems/base.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/ecosystems/claude.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/ecosystems/codex.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/ecosystems/detect.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/ecosystems/gemini.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/ecosystems/opencode.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/ecosystems/registry.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/ecosystems/types.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/github_reporting.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/__init__.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/adapters/__init__.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/adapters/antigravity.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/adapters/base.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/adapters/claude_code.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/adapters/cloud_identity.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/adapters/codex.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/adapters/copilot.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/adapters/cursor.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/adapters/gemini.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/adapters/hermes.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/adapters/mcp_servers.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/adapters/openclaw.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/adapters/openclaw_config.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/adapters/openclaw_support.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/adapters/opencode.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/adapters/opencode_artifacts.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/advisory_model.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/approvals.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/bridge/__init__.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/capabilities.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/cli/__init__.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/cli/approval_commands.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/cli/bootstrap.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/cli/connect_flow.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/cli/install_commands.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/cli/product.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/cli/prompt.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/cli/update_commands.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/codex_config.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/consumer/__init__.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/consumer/service.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/daemon/__init__.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/daemon/client.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/daemon/manager.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/daemon/server.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/daemon/static/apple-touch-icon.png +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/daemon/static/assets/guard-dashboard.js +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/daemon/static/assets/index.css +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/daemon/static/brand/Logo_Icon_Dark.png +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/daemon/static/brand/Logo_Whole.png +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/daemon/static/favicon-16x16.png +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/daemon/static/favicon-32x32.png +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/daemon/static/favicon.ico +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/daemon/static/index.html +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/edge_events.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/incident.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/launcher.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/mcp_tool_calls.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/models.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/policy/__init__.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/policy/engine.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/protect.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/proxy/__init__.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/proxy/remote.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/proxy/runtime_mcp.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/proxy/stdio.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/receipts/__init__.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/receipts/manager.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/redaction.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/runtime/__init__.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/runtime/decisions.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/runtime/signals.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/runtime/surface_server.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/schemas/__init__.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/schemas/consumer_mode.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/schemas/guard_event_v1.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/schemas/surface_server.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/shims.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/store.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/store_approvals.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/store_connect.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/types.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/integrations/__init__.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/integrations/cisco_mcp_scanner.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/integrations/cisco_skill_scanner.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/lint_fixes.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/marketplace_support.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/models.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/path_support.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/policy.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/quality_artifact.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/repo_detect.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/reporting.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/rules/__init__.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/rules/registry.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/rules/specs.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/scanner.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/submission.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/suppressions.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/trust_domain_scoring.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/trust_helpers.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/trust_mcp_scoring.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/trust_models.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/trust_plugin_scoring.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/trust_scoring.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/trust_skill_scoring.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/trust_specs.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/verification.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/__init__.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/conftest.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/__init__.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/bad-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/bad-plugin/.mcp.json +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/bad-plugin/secrets.js +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/claude-plugin-good/.claude-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/claude-plugin-good/LICENSE +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/claude-plugin-good/README.md +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/claude-plugin-good/SECURITY.md +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/claude-plugin-good/hooks/hooks.json +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/claude-plugin-good/skills/example/SKILL.md +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/code-quality-bad/evil.js +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/code-quality-bad/inject.js +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/gemini-extension-good/GEMINI.md +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/gemini-extension-good/LICENSE +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/gemini-extension-good/README.md +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/gemini-extension-good/SECURITY.md +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/gemini-extension-good/commands/hello.toml +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/gemini-extension-good/gemini-extension.json +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/good-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/good-plugin/.codexignore +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/good-plugin/LICENSE +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/good-plugin/README.md +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/good-plugin/SECURITY.md +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/good-plugin/assets/icon.svg +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/good-plugin/assets/logo.svg +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/good-plugin/assets/screenshot.svg +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/good-plugin/skills/example/SKILL.md +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/guard-codex-malicious-mcp/.codex/config.toml +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/hermes-plugin-evil/config.yaml +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/hermes-plugin-evil/mcp_servers.json +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/hermes-plugin-evil/skills/security/malicious/SKILL.md +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/SKILL.md +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/references/api-setup.md +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/scripts/deploy.sh +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/hermes-plugin-evil/skills/utils/benign/SKILL.md +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/malformed-json/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/malicious-skill-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/malicious-skill-plugin/.codexignore +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/malicious-skill-plugin/LICENSE +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/malicious-skill-plugin/README.md +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/malicious-skill-plugin/SECURITY.md +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/malicious-skill-plugin/skills/leaky-skill/SKILL.md +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/mcp-canary-server.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/minimal-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/missing-fields/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/mit-license/LICENSE +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/multi-ecosystem-repo/codex-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/multi-ecosystem-repo/codex-plugin/LICENSE +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/multi-ecosystem-repo/codex-plugin/README.md +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/multi-ecosystem-repo/codex-plugin/SECURITY.md +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/multi-ecosystem-repo/gemini-ext/README.md +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/multi-ecosystem-repo/gemini-ext/gemini-extension.json +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/multi-plugin-repo/.agents/plugins/marketplace.json +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/.codexignore +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/LICENSE +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/README.md +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/SECURITY.md +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/skills/example/SKILL.md +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/multi-plugin-repo/plugins/beta-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/multi-plugin-repo/plugins/beta-plugin/skills/example/SKILL.md +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/no-version/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/opencode-good/.opencode/commands/hello.md +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/opencode-good/.opencode/plugins/example.ts +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/opencode-good/LICENSE +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/opencode-good/README.md +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/opencode-good/SECURITY.md +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/opencode-good/opencode.jsonc +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/skills-missing-dir/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/skills-no-frontmatter/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/skills-no-frontmatter/skills/bad-skill/SKILL.md +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/with-marketplace/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/with-marketplace/marketplace-broken.json +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/fixtures/with-marketplace/marketplace.json +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test-trust-scoring.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test-trust-specs.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_action_runner.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_best_practices.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_cisco_install_surfaces.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_cli.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_code_quality.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_config.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_coverage_remaining.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_ecosystems.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_edge_cases.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_final_coverage.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_guard_approvals.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_guard_bootstrap.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_guard_capabilities.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_guard_claude_adapter.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_guard_codex_e2e.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_guard_codex_install.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_guard_codex_proxy.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_guard_connect_flow.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_guard_consumer_mode.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_guard_copilot_adapter.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_guard_copilot_proxy.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_guard_daemon_manager.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_guard_event_schema_v1.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_guard_events.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_guard_launch_env.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_guard_opencode_proxy.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_guard_product_flow.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_guard_protect.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_guard_render.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_guard_runtime_action_harnesses.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_guard_runtime_decisions.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_guard_runtime_signals.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_guard_store_migrations.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_guard_surface_server.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_guard_verdicts.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_hermes_adapter.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_integration.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_lint_fixes.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_live_cisco_smoke.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_manifest.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_marketplace.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_mcp_security.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_openclaw_adapter.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_operational_security.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_policy.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_quality_artifact.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_rule_registry.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_scanner.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_schema_contracts.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_security.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_security_ops.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_skill_security.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_submission.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_trust_scoring.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_trust_specs.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_verification.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/tests/test_versioning.py +0 -0
- {plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/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.106
|
|
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.106"
|
|
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.106"
|
|
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.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/cli/commands.py
RENAMED
|
@@ -1043,10 +1043,12 @@ def run_guard_command(
|
|
|
1043
1043
|
if args.harness:
|
|
1044
1044
|
adapter = get_adapter(args.harness)
|
|
1045
1045
|
payload = adapter.diagnostics(context)
|
|
1046
|
+
payload["runtime_detector_registry"] = _runtime_detector_registry_payload(config)
|
|
1046
1047
|
else:
|
|
1047
1048
|
payload = {
|
|
1048
1049
|
"tables": store.list_table_names(),
|
|
1049
1050
|
"adapters": [detection.to_dict() for detection in detect_all(context)],
|
|
1051
|
+
"runtime_detector_registry": _runtime_detector_registry_payload(config),
|
|
1050
1052
|
}
|
|
1051
1053
|
_emit("doctor", payload, getattr(args, "json", False))
|
|
1052
1054
|
return 0
|
|
@@ -3049,6 +3051,15 @@ def _guard_cli_settings_payload(config: GuardConfig) -> dict[str, object]:
|
|
|
3049
3051
|
}
|
|
3050
3052
|
|
|
3051
3053
|
|
|
3054
|
+
def _runtime_detector_registry_payload(config: GuardConfig) -> dict[str, object]:
|
|
3055
|
+
return {
|
|
3056
|
+
"enabled": config.runtime_detector_registry,
|
|
3057
|
+
"debug_trace": config.runtime_detector_debug_trace,
|
|
3058
|
+
"timeout_ms": config.runtime_detector_timeout_ms,
|
|
3059
|
+
"disabled_detector_ids": list(config.runtime_detector_disabled_ids),
|
|
3060
|
+
}
|
|
3061
|
+
|
|
3062
|
+
|
|
3052
3063
|
def _update_guard_cli_settings(*, args: argparse.Namespace, config: GuardConfig, guard_home: Path) -> GuardConfig:
|
|
3053
3064
|
settings_command = getattr(args, "settings_set_command", None)
|
|
3054
3065
|
if settings_command == "security-level":
|
{plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/cli/render.py
RENAMED
|
@@ -293,6 +293,11 @@ def _render_doctor(console: Console, payload: dict[str, object]) -> None:
|
|
|
293
293
|
summary.add_row("Installed", _bool_label(bool(payload.get("installed"))))
|
|
294
294
|
summary.add_row("Command", _bool_label(bool(payload.get("command_available"))))
|
|
295
295
|
summary.add_row("Artifacts", str(len(_coerce_dict_list(payload.get("artifacts")))))
|
|
296
|
+
registry = payload.get("runtime_detector_registry")
|
|
297
|
+
if isinstance(registry, dict):
|
|
298
|
+
registry_state = "enabled" if bool(registry.get("enabled")) else "disabled"
|
|
299
|
+
timeout_ms = registry.get("timeout_ms")
|
|
300
|
+
summary.add_row("Detector registry", f"{registry_state}, {timeout_ms} ms")
|
|
296
301
|
summary.add_row("Warnings", str(len(warnings)))
|
|
297
302
|
console.print(Panel(summary, title="Guard doctor", border_style="cyan"))
|
|
298
303
|
if warnings:
|
|
@@ -178,6 +178,7 @@ class GuardConfig:
|
|
|
178
178
|
billing: bool = False
|
|
179
179
|
runtime_detector_registry: bool = False
|
|
180
180
|
runtime_detector_timeout_ms: int = 50
|
|
181
|
+
runtime_detector_debug_trace: bool = False
|
|
181
182
|
runtime_detector_disabled_ids: tuple[str, ...] = ()
|
|
182
183
|
risk_actions: dict[str, GuardAction] | None = None
|
|
183
184
|
harness_risk_actions: dict[str, dict[str, GuardAction]] | None = None
|
|
@@ -257,6 +258,7 @@ def load_guard_config(guard_home: Path, workspace: Path | None = None) -> GuardC
|
|
|
257
258
|
billing=bool(merged.get("billing", False)),
|
|
258
259
|
runtime_detector_registry=_coerce_loaded_bool(merged.get("runtime_detector_registry", False)),
|
|
259
260
|
runtime_detector_timeout_ms=_coerce_loaded_positive_int(merged.get("runtime_detector_timeout_ms", 50), 50),
|
|
261
|
+
runtime_detector_debug_trace=_coerce_loaded_bool(merged.get("runtime_detector_debug_trace", False)),
|
|
260
262
|
runtime_detector_disabled_ids=_coerce_loaded_string_tuple(merged.get("runtime_detector_disabled_ids")),
|
|
261
263
|
harness_actions=_coerce_action_map(merged.get("harnesses")),
|
|
262
264
|
publisher_actions=_coerce_action_map(merged.get("publishers")),
|
|
@@ -8,6 +8,7 @@ from pathlib import PurePath
|
|
|
8
8
|
from urllib.parse import urlsplit
|
|
9
9
|
|
|
10
10
|
from .models import GuardArtifact
|
|
11
|
+
from .runtime.secret_sensitivity import classify_legacy_secret_path_families
|
|
11
12
|
from .runtime.signals import RiskSignalV2
|
|
12
13
|
from .types import GuardSignal
|
|
13
14
|
|
|
@@ -305,12 +306,7 @@ def extract_network_hosts(text: str) -> set[str]:
|
|
|
305
306
|
def classify_secret_paths(text: str) -> set[str]:
|
|
306
307
|
"""Classify secret-bearing file families referenced in text."""
|
|
307
308
|
|
|
308
|
-
|
|
309
|
-
classes: set[str] = set()
|
|
310
|
-
for pattern, label in _SECRET_PATH_LABELS:
|
|
311
|
-
if pattern in lowered:
|
|
312
|
-
classes.add(label)
|
|
313
|
-
return classes
|
|
309
|
+
return classify_legacy_secret_path_families(text)
|
|
314
310
|
|
|
315
311
|
|
|
316
312
|
def detect_encoded_command(text: str) -> list[GuardSignal]:
|
{plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/runtime/actions.py
RENAMED
|
@@ -11,6 +11,7 @@ from pathlib import Path, PureWindowsPath
|
|
|
11
11
|
from typing import Literal
|
|
12
12
|
|
|
13
13
|
from ..redaction import redact_text
|
|
14
|
+
from .secret_sensitivity import redacted_secret_path_context
|
|
14
15
|
|
|
15
16
|
GuardActionType = Literal[
|
|
16
17
|
"prompt",
|
|
@@ -43,7 +44,23 @@ _SCHEMA_VERSION = 1
|
|
|
43
44
|
_SHELL_TOOL_NAMES = frozenset({"bash", "shell", "sh", "zsh", "terminal", "run_command", "run_terminal_command"})
|
|
44
45
|
_FILE_READ_TOOL_NAMES = frozenset({"read", "read_file", "open_file", "view", "view_file", "cat_file"})
|
|
45
46
|
_FILE_WRITE_TOOL_NAMES = frozenset({"write", "edit", "multiedit", "write_file", "edit_file"})
|
|
46
|
-
_PATH_KEYS = (
|
|
47
|
+
_PATH_KEYS = (
|
|
48
|
+
"path",
|
|
49
|
+
"paths",
|
|
50
|
+
"file_path",
|
|
51
|
+
"file_paths",
|
|
52
|
+
"filePath",
|
|
53
|
+
"filePaths",
|
|
54
|
+
"filepath",
|
|
55
|
+
"file",
|
|
56
|
+
"files",
|
|
57
|
+
"filename",
|
|
58
|
+
"filenames",
|
|
59
|
+
"target_path",
|
|
60
|
+
"target_paths",
|
|
61
|
+
"targetPath",
|
|
62
|
+
"targetPaths",
|
|
63
|
+
)
|
|
47
64
|
_COMMAND_KEYS = ("command", "cmd", "shell_command", "shellCommand")
|
|
48
65
|
_SENSITIVE_RAW_KEYS = frozenset(
|
|
49
66
|
{
|
|
@@ -652,6 +669,8 @@ def _target_paths(
|
|
|
652
669
|
value = tool_input.get(key)
|
|
653
670
|
if isinstance(value, str) and value.strip():
|
|
654
671
|
paths.append(value.strip())
|
|
672
|
+
elif isinstance(value, list):
|
|
673
|
+
paths.extend(item.strip() for item in value if isinstance(item, str) and item.strip())
|
|
655
674
|
for text in (command, prompt_text):
|
|
656
675
|
if text is not None:
|
|
657
676
|
paths.extend(match.group("path") for match in _PROMPT_PATH_PATTERN.finditer(text))
|
|
@@ -687,14 +706,25 @@ def _redacted_target_path(path: str, *, home_dir: Path | str | None) -> str | No
|
|
|
687
706
|
if stripped == "~" or stripped.startswith("~/"):
|
|
688
707
|
return redact_text(stripped).text
|
|
689
708
|
if stripped.startswith("~"):
|
|
709
|
+
secret_context = redacted_secret_path_context(stripped)
|
|
710
|
+
if secret_context is not None:
|
|
711
|
+
return secret_context
|
|
690
712
|
target_name = Path(stripped).name or "path"
|
|
691
713
|
return f".../{target_name}"
|
|
692
714
|
windows_path = PureWindowsPath(stripped)
|
|
693
715
|
if windows_path.is_absolute():
|
|
716
|
+
secret_context = redacted_secret_path_context(stripped)
|
|
717
|
+
if secret_context is not None:
|
|
718
|
+
return secret_context
|
|
694
719
|
target_name = windows_path.name or "path"
|
|
695
720
|
return f".../{target_name}"
|
|
696
721
|
if _is_absolute_target_path(stripped):
|
|
697
|
-
|
|
722
|
+
redacted_path = redacted_workspace_label(stripped, home_dir=home_dir)
|
|
723
|
+
if redacted_path.startswith(".../"):
|
|
724
|
+
secret_context = redacted_secret_path_context(stripped)
|
|
725
|
+
if secret_context is not None:
|
|
726
|
+
return secret_context
|
|
727
|
+
return redacted_path
|
|
698
728
|
return redact_text(stripped).text
|
|
699
729
|
|
|
700
730
|
|
|
@@ -10,6 +10,7 @@ from typing import Literal, Protocol
|
|
|
10
10
|
|
|
11
11
|
from codex_plugin_scanner.guard.config import GuardConfig
|
|
12
12
|
from codex_plugin_scanner.guard.runtime.actions import GuardActionEnvelope
|
|
13
|
+
from codex_plugin_scanner.guard.runtime.secret_sensitivity import SecretPathMatch, classify_secret_path
|
|
13
14
|
from codex_plugin_scanner.guard.runtime.signals import RiskSignalCategory, RiskSignalV2
|
|
14
15
|
|
|
15
16
|
DETECTOR_CATEGORY_TAGS: tuple[RiskSignalCategory, ...] = (
|
|
@@ -124,14 +125,55 @@ class DetectorRegistry:
|
|
|
124
125
|
return DetectorRunResult(signals=tuple(signals), telemetry=tuple(telemetry))
|
|
125
126
|
|
|
126
127
|
|
|
128
|
+
class SecretPathDetector:
|
|
129
|
+
detector_id = "secret.path"
|
|
130
|
+
categories: tuple[RiskSignalCategory, ...] = ("secret",)
|
|
131
|
+
|
|
132
|
+
def detect(self, action: GuardActionEnvelope, context: DetectorContext) -> tuple[RiskSignalV2, ...]:
|
|
133
|
+
if action.action_type != "file_read":
|
|
134
|
+
return ()
|
|
135
|
+
matches = tuple(_secret_path_matches(action.target_paths, workspace=context.workspace))
|
|
136
|
+
return tuple(_secret_path_signal(match, index=index) for index, match in enumerate(matches))
|
|
137
|
+
|
|
138
|
+
|
|
127
139
|
def register_default_detectors() -> tuple[GuardDetector, ...]:
|
|
128
|
-
return ()
|
|
140
|
+
return (SecretPathDetector(),)
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def _secret_path_signal(match: SecretPathMatch, *, index: int) -> RiskSignalV2:
|
|
144
|
+
return RiskSignalV2(
|
|
145
|
+
signal_id=f"secret:path:{_slug(match.family)}:{index}",
|
|
146
|
+
category="secret",
|
|
147
|
+
severity="high",
|
|
148
|
+
confidence="strong",
|
|
149
|
+
detector="secret.path",
|
|
150
|
+
title=f"Direct access to {match.family}",
|
|
151
|
+
plain_reason=f"Requested direct access to {match.family}.",
|
|
152
|
+
technical_detail=f"matched secret path family: {match.family}",
|
|
153
|
+
evidence_ref="target_paths",
|
|
154
|
+
redaction_level="summary",
|
|
155
|
+
false_positive_hint="Allow only if this tool needs the exact local secret file for the current task.",
|
|
156
|
+
advisory_id=None,
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def _secret_path_matches(paths: Sequence[str], *, workspace: Path | None) -> tuple[SecretPathMatch, ...]:
|
|
161
|
+
matches: list[SecretPathMatch] = []
|
|
162
|
+
for path in paths:
|
|
163
|
+
match = classify_secret_path(path, cwd=workspace)
|
|
164
|
+
if match is not None:
|
|
165
|
+
matches.append(match)
|
|
166
|
+
return tuple(matches)
|
|
129
167
|
|
|
130
168
|
|
|
131
169
|
def _elapsed_ms(started_at: float, finished_at: float) -> int:
|
|
132
170
|
return max(0, round((finished_at - started_at) * 1000))
|
|
133
171
|
|
|
134
172
|
|
|
173
|
+
def _slug(value: str) -> str:
|
|
174
|
+
return "-".join(part for part in value.lower().replace(".", " ").replace("/", " ").split() if part)
|
|
175
|
+
|
|
176
|
+
|
|
135
177
|
def _telemetry(
|
|
136
178
|
detector: GuardDetector,
|
|
137
179
|
status: DetectorRunStatus,
|
{plugin_scanner-2.0.104 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/runtime/runner.py
RENAMED
|
@@ -26,7 +26,7 @@ from ..models import GuardArtifact, HarnessDetection, PolicyDecision
|
|
|
26
26
|
from ..store import GuardStore
|
|
27
27
|
from ..types import PromptRequest, RemediationAction
|
|
28
28
|
from .actions import GuardActionEnvelope, redacted_workspace_label
|
|
29
|
-
from .detectors import DetectorContext, DetectorRegistry, register_default_detectors
|
|
29
|
+
from .detectors import DetectorContext, DetectorRegistry, DetectorRunResult, register_default_detectors
|
|
30
30
|
|
|
31
31
|
_APPROVAL_METADATA_KEYS = (
|
|
32
32
|
"approval_center_url",
|
|
@@ -365,13 +365,63 @@ def _evaluation_with_detector_registry(
|
|
|
365
365
|
timeout_ms=config.runtime_detector_timeout_ms,
|
|
366
366
|
disabled_detector_ids=config.runtime_detector_disabled_ids,
|
|
367
367
|
)
|
|
368
|
+
trace_error = (
|
|
369
|
+
_write_detector_debug_trace(config, action_envelope, result) if config.runtime_detector_debug_trace else None
|
|
370
|
+
)
|
|
368
371
|
if not result.signals and not result.telemetry:
|
|
369
|
-
|
|
370
|
-
|
|
372
|
+
if trace_error is None:
|
|
373
|
+
return evaluation
|
|
374
|
+
return {**evaluation, "runtime_detector_trace_error": trace_error}
|
|
375
|
+
next_evaluation = {
|
|
371
376
|
**evaluation,
|
|
372
377
|
"runtime_detector_signals_v2": [signal.to_dict() for signal in result.signals],
|
|
373
378
|
"runtime_detector_telemetry": [item.to_dict() for item in result.telemetry],
|
|
374
379
|
}
|
|
380
|
+
if trace_error is not None:
|
|
381
|
+
next_evaluation["runtime_detector_trace_error"] = trace_error
|
|
382
|
+
return next_evaluation
|
|
383
|
+
|
|
384
|
+
|
|
385
|
+
def _write_detector_debug_trace(
|
|
386
|
+
config: GuardConfig,
|
|
387
|
+
action_envelope: GuardActionEnvelope,
|
|
388
|
+
result: DetectorRunResult,
|
|
389
|
+
) -> dict[str, object] | None:
|
|
390
|
+
created_at = datetime.now(timezone.utc)
|
|
391
|
+
action_payload = action_envelope.to_dict()
|
|
392
|
+
trace_payload = {
|
|
393
|
+
"schema_version": 1,
|
|
394
|
+
"created_at": created_at.isoformat(),
|
|
395
|
+
"action": _redact_detector_debug_payload(action_payload),
|
|
396
|
+
"signals": [signal.to_dict() for signal in result.signals],
|
|
397
|
+
"telemetry": [item.to_dict() for item in result.telemetry],
|
|
398
|
+
}
|
|
399
|
+
trace_dir = config.guard_home / "debug" / "detectors"
|
|
400
|
+
action_digest = hashlib.sha256(
|
|
401
|
+
json.dumps(action_payload, sort_keys=True, default=str).encode("utf-8"),
|
|
402
|
+
).hexdigest()[:12]
|
|
403
|
+
trace_path = trace_dir / f"{created_at.strftime('%Y%m%dT%H%M%S%fZ')}-{action_digest}.json"
|
|
404
|
+
try:
|
|
405
|
+
trace_dir.mkdir(parents=True, exist_ok=True)
|
|
406
|
+
trace_path.write_text(json.dumps(trace_payload, indent=2, sort_keys=True) + "\n", encoding="utf-8")
|
|
407
|
+
except OSError as error:
|
|
408
|
+
return {"error_type": type(error).__name__, "message": str(error)}
|
|
409
|
+
return None
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
def _redact_detector_debug_payload(value: object) -> object:
|
|
413
|
+
if isinstance(value, dict):
|
|
414
|
+
redacted: dict[str, object] = {}
|
|
415
|
+
for key, item in value.items():
|
|
416
|
+
key_text = str(key)
|
|
417
|
+
if "prompt" in key_text.lower():
|
|
418
|
+
redacted[key_text] = "[redacted]"
|
|
419
|
+
else:
|
|
420
|
+
redacted[key_text] = _redact_detector_debug_payload(item)
|
|
421
|
+
return redacted
|
|
422
|
+
if isinstance(value, (tuple, list)):
|
|
423
|
+
return [_redact_detector_debug_payload(item) for item in value]
|
|
424
|
+
return value
|
|
375
425
|
|
|
376
426
|
|
|
377
427
|
def _guard_run_config_paths(
|
|
@@ -16,6 +16,8 @@ from dataclasses import dataclass
|
|
|
16
16
|
from pathlib import Path
|
|
17
17
|
|
|
18
18
|
from ..models import GuardArtifact
|
|
19
|
+
from .secret_sensitivity import SecretPathMatch as SensitivePathMatch
|
|
20
|
+
from .secret_sensitivity import classify_secret_path
|
|
19
21
|
|
|
20
22
|
_FILE_READ_TOOL_NAMES = frozenset(
|
|
21
23
|
{
|
|
@@ -359,16 +361,6 @@ _SENSITIVE_PATH_REASONS = {
|
|
|
359
361
|
}
|
|
360
362
|
|
|
361
363
|
|
|
362
|
-
@dataclass(frozen=True, slots=True)
|
|
363
|
-
class SensitivePathMatch:
|
|
364
|
-
"""A normalized sensitive path classification."""
|
|
365
|
-
|
|
366
|
-
requested_path: str
|
|
367
|
-
normalized_path: str
|
|
368
|
-
path_class: str
|
|
369
|
-
reason: str
|
|
370
|
-
|
|
371
|
-
|
|
372
364
|
@dataclass(frozen=True, slots=True)
|
|
373
365
|
class FileReadRequestMatch:
|
|
374
366
|
"""A sensitive file-read tool call."""
|
|
@@ -405,41 +397,7 @@ def classify_sensitive_path(
|
|
|
405
397
|
) -> SensitivePathMatch | None:
|
|
406
398
|
"""Classify a path if it points at a high-confidence sensitive local file."""
|
|
407
399
|
|
|
408
|
-
|
|
409
|
-
return None
|
|
410
|
-
requested_path = path.strip().strip("'").strip('"')
|
|
411
|
-
if not requested_path:
|
|
412
|
-
return None
|
|
413
|
-
expanded_home = _expand_home(requested_path, home_dir)
|
|
414
|
-
normalized_path = _normalize_path(expanded_home, cwd)
|
|
415
|
-
lowered_segments = tuple(segment for segment in normalized_path.replace("\\", "/").lower().split("/") if segment)
|
|
416
|
-
if not lowered_segments:
|
|
417
|
-
return None
|
|
418
|
-
basename = lowered_segments[-1]
|
|
419
|
-
if basename == ".env" or basename.startswith(".env."):
|
|
420
|
-
return SensitivePathMatch(
|
|
421
|
-
requested_path=requested_path,
|
|
422
|
-
normalized_path=normalized_path,
|
|
423
|
-
path_class="local .env file",
|
|
424
|
-
reason=_SENSITIVE_PATH_REASONS["local .env file"],
|
|
425
|
-
)
|
|
426
|
-
if basename in _SENSITIVE_BASENAME_LABELS:
|
|
427
|
-
path_class = _SENSITIVE_BASENAME_LABELS[basename]
|
|
428
|
-
return SensitivePathMatch(
|
|
429
|
-
requested_path=requested_path,
|
|
430
|
-
normalized_path=normalized_path,
|
|
431
|
-
path_class=path_class,
|
|
432
|
-
reason=_SENSITIVE_PATH_REASONS[path_class],
|
|
433
|
-
)
|
|
434
|
-
for suffix, path_class in _SENSITIVE_SUFFIX_LABELS.items():
|
|
435
|
-
if lowered_segments[-len(suffix) :] == suffix:
|
|
436
|
-
return SensitivePathMatch(
|
|
437
|
-
requested_path=requested_path,
|
|
438
|
-
normalized_path=normalized_path,
|
|
439
|
-
path_class=path_class,
|
|
440
|
-
reason=_SENSITIVE_PATH_REASONS[path_class],
|
|
441
|
-
)
|
|
442
|
-
return None
|
|
400
|
+
return classify_secret_path(path, cwd=cwd, home_dir=home_dir)
|
|
443
401
|
|
|
444
402
|
|
|
445
403
|
def extract_sensitive_file_read_request(
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
"""Shared secret path family classification for Guard runtime surfaces."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Literal
|
|
9
|
+
|
|
10
|
+
SecretSensitivity = Literal["high", "critical"]
|
|
11
|
+
|
|
12
|
+
_AWS_CREDENTIALS_MARKER = "/".join((".aws", "credentials"))
|
|
13
|
+
_DOCKER_CONFIG_MARKER = "/".join((".docker", "config.json"))
|
|
14
|
+
_KUBE_CONFIG_MARKER = "/".join((".kube", "config"))
|
|
15
|
+
|
|
16
|
+
SECRET_PATH_TEXT_MARKERS: tuple[tuple[str, str], ...] = (
|
|
17
|
+
(".env", "local .env file"),
|
|
18
|
+
(".npmrc", "npm registry credentials"),
|
|
19
|
+
(".pypirc", "Python package credentials"),
|
|
20
|
+
(_AWS_CREDENTIALS_MARKER, "AWS shared credentials file"),
|
|
21
|
+
(".ssh/", "SSH private key"),
|
|
22
|
+
(".gnupg/", "GnuPG key material"),
|
|
23
|
+
(_DOCKER_CONFIG_MARKER, "Docker client config"),
|
|
24
|
+
(_KUBE_CONFIG_MARKER, "Kubernetes config"),
|
|
25
|
+
("terraform.tfvars", "Terraform variable secrets"),
|
|
26
|
+
(".git-credentials", "Git credential store"),
|
|
27
|
+
)
|
|
28
|
+
LEGACY_SECRET_PATH_TEXT_MARKERS: tuple[tuple[str, str], ...] = (
|
|
29
|
+
(".env", "local .env file"),
|
|
30
|
+
(".npmrc", "npm registry credentials"),
|
|
31
|
+
(".pypirc", "python package credentials"),
|
|
32
|
+
(".aws/" + "credentials", "aws shared credentials"),
|
|
33
|
+
(".ssh/", "ssh material"),
|
|
34
|
+
(".gnupg/", "gpg material"),
|
|
35
|
+
(".docker/" + "config.json", "docker credentials"),
|
|
36
|
+
(".kube/config", "kubeconfig"),
|
|
37
|
+
(".git-credentials", "git credential store"),
|
|
38
|
+
)
|
|
39
|
+
_SENSITIVE_BASENAME_LABELS = {
|
|
40
|
+
".npmrc": "npm registry credentials",
|
|
41
|
+
".pypirc": "Python package credentials",
|
|
42
|
+
".netrc": "netrc credentials",
|
|
43
|
+
".git-credentials": "Git credential store",
|
|
44
|
+
".terraform.tfvars": "Terraform variable secrets",
|
|
45
|
+
"terraform.tfvars": "Terraform variable secrets",
|
|
46
|
+
}
|
|
47
|
+
_REDACTED_BASENAME_LABELS = {
|
|
48
|
+
"id_rsa": "SSH private key",
|
|
49
|
+
"id_ed25519": "SSH private key",
|
|
50
|
+
"id_ecdsa": "SSH private key",
|
|
51
|
+
}
|
|
52
|
+
_SENSITIVE_SUFFIX_LABELS = {
|
|
53
|
+
(".aws", "credentials"): "AWS shared credentials file",
|
|
54
|
+
(".aws", "config"): "AWS shared config file",
|
|
55
|
+
(".docker", "config.json"): "Docker client config",
|
|
56
|
+
(".kube", "config"): "Kubernetes config",
|
|
57
|
+
(".ssh", "id_rsa"): "SSH private key",
|
|
58
|
+
(".ssh", "id_ed25519"): "SSH private key",
|
|
59
|
+
(".ssh", "id_ecdsa"): "SSH private key",
|
|
60
|
+
(".ssh", "config"): "SSH client config",
|
|
61
|
+
}
|
|
62
|
+
_SENSITIVE_DIRECTORY_LABELS = {
|
|
63
|
+
".gnupg": "GnuPG key material",
|
|
64
|
+
}
|
|
65
|
+
_SENSITIVE_PATH_REASONS = {
|
|
66
|
+
"local .env file": "Guard treats .env files as sensitive because they commonly store local secrets.",
|
|
67
|
+
"npm registry credentials": "Guard treats .npmrc as sensitive because it may contain registry tokens.",
|
|
68
|
+
"Python package credentials": "Guard treats .pypirc as sensitive because it may contain package credentials.",
|
|
69
|
+
"netrc credentials": "Guard treats .netrc as sensitive because it may contain login secrets.",
|
|
70
|
+
"Git credential store": "Guard treats .git-credentials as sensitive because it may contain repository credentials.",
|
|
71
|
+
"AWS shared credentials file": (
|
|
72
|
+
"Guard treats AWS shared credentials as sensitive because they contain cloud access keys."
|
|
73
|
+
),
|
|
74
|
+
"AWS shared config file": "Guard treats AWS shared config as sensitive because it may contain credential profiles.",
|
|
75
|
+
"Docker client config": "Guard treats Docker client config as sensitive because it may contain registry auth.",
|
|
76
|
+
"Kubernetes config": "Guard treats Kubernetes config as sensitive because it may include cluster credentials.",
|
|
77
|
+
"SSH private key": "Guard treats SSH private keys as sensitive because they provide direct host access.",
|
|
78
|
+
"SSH client config": "Guard treats SSH config as sensitive because it may reveal or shape host credentials.",
|
|
79
|
+
"GnuPG key material": "Guard treats GnuPG key material as sensitive because it can unlock encrypted assets.",
|
|
80
|
+
"Terraform variable secrets": (
|
|
81
|
+
"Guard treats Terraform variable files as sensitive because they often contain secrets."
|
|
82
|
+
),
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
@dataclass(frozen=True, slots=True)
|
|
87
|
+
class SecretPathMatch:
|
|
88
|
+
family: str
|
|
89
|
+
path: str
|
|
90
|
+
sensitivity: SecretSensitivity
|
|
91
|
+
reason: str
|
|
92
|
+
requested_path: str = ""
|
|
93
|
+
|
|
94
|
+
@property
|
|
95
|
+
def normalized_path(self) -> str:
|
|
96
|
+
return self.path
|
|
97
|
+
|
|
98
|
+
@property
|
|
99
|
+
def path_class(self) -> str:
|
|
100
|
+
return self.family
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def classify_secret_path(
|
|
104
|
+
path: str | None,
|
|
105
|
+
*,
|
|
106
|
+
cwd: Path | None = None,
|
|
107
|
+
home_dir: Path | None = None,
|
|
108
|
+
) -> SecretPathMatch | None:
|
|
109
|
+
if not isinstance(path, str):
|
|
110
|
+
return None
|
|
111
|
+
requested_path = path.strip().strip("'").strip('"')
|
|
112
|
+
if not requested_path:
|
|
113
|
+
return None
|
|
114
|
+
expanded_home = _expand_home(requested_path, home_dir)
|
|
115
|
+
normalized_path = _normalize_path(expanded_home, cwd)
|
|
116
|
+
lowered_segments = tuple(segment for segment in normalized_path.replace("\\", "/").lower().split("/") if segment)
|
|
117
|
+
if not lowered_segments:
|
|
118
|
+
return None
|
|
119
|
+
basename = lowered_segments[-1]
|
|
120
|
+
if basename == ".env" or basename.startswith(".env."):
|
|
121
|
+
return _match(
|
|
122
|
+
requested_path=requested_path,
|
|
123
|
+
normalized_path=normalized_path,
|
|
124
|
+
family="local .env file",
|
|
125
|
+
sensitivity="critical",
|
|
126
|
+
)
|
|
127
|
+
if basename in _SENSITIVE_BASENAME_LABELS:
|
|
128
|
+
return _match(
|
|
129
|
+
requested_path=requested_path,
|
|
130
|
+
normalized_path=normalized_path,
|
|
131
|
+
family=_SENSITIVE_BASENAME_LABELS[basename],
|
|
132
|
+
sensitivity="high",
|
|
133
|
+
)
|
|
134
|
+
if "..." in lowered_segments and basename in _REDACTED_BASENAME_LABELS:
|
|
135
|
+
family = _REDACTED_BASENAME_LABELS[basename]
|
|
136
|
+
sensitivity: SecretSensitivity = "critical" if family == "SSH private key" else "high"
|
|
137
|
+
return _match(
|
|
138
|
+
requested_path=requested_path,
|
|
139
|
+
normalized_path=normalized_path,
|
|
140
|
+
family=family,
|
|
141
|
+
sensitivity=sensitivity,
|
|
142
|
+
)
|
|
143
|
+
for directory, family in _SENSITIVE_DIRECTORY_LABELS.items():
|
|
144
|
+
if directory in lowered_segments:
|
|
145
|
+
return _match(
|
|
146
|
+
requested_path=requested_path,
|
|
147
|
+
normalized_path=normalized_path,
|
|
148
|
+
family=family,
|
|
149
|
+
sensitivity="high",
|
|
150
|
+
)
|
|
151
|
+
for suffix, family in _SENSITIVE_SUFFIX_LABELS.items():
|
|
152
|
+
if lowered_segments[-len(suffix) :] == suffix:
|
|
153
|
+
sensitivity: SecretSensitivity = "critical" if family == "SSH private key" else "high"
|
|
154
|
+
return _match(
|
|
155
|
+
requested_path=requested_path,
|
|
156
|
+
normalized_path=normalized_path,
|
|
157
|
+
family=family,
|
|
158
|
+
sensitivity=sensitivity,
|
|
159
|
+
)
|
|
160
|
+
return None
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def classify_secret_path_families(text: str) -> set[str]:
|
|
164
|
+
lowered = text.lower()
|
|
165
|
+
return {family for marker, family in SECRET_PATH_TEXT_MARKERS if marker in lowered}
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def classify_legacy_secret_path_families(text: str) -> set[str]:
|
|
169
|
+
lowered = text.lower()
|
|
170
|
+
return {family for marker, family in LEGACY_SECRET_PATH_TEXT_MARKERS if marker in lowered}
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def redacted_secret_path_context(path: str) -> str | None:
|
|
174
|
+
segments = tuple(segment for segment in path.replace("\\", "/").split("/") if segment)
|
|
175
|
+
lowered_segments = tuple(segment.lower() for segment in segments)
|
|
176
|
+
if not lowered_segments:
|
|
177
|
+
return None
|
|
178
|
+
for suffix in _SENSITIVE_SUFFIX_LABELS:
|
|
179
|
+
if lowered_segments[-len(suffix) :] == suffix:
|
|
180
|
+
return ".../" + "/".join(suffix)
|
|
181
|
+
for directory in _SENSITIVE_DIRECTORY_LABELS:
|
|
182
|
+
if directory in lowered_segments and len(segments) > 1:
|
|
183
|
+
return f".../{directory}/{segments[-1]}"
|
|
184
|
+
return None
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def _match(
|
|
188
|
+
*,
|
|
189
|
+
requested_path: str,
|
|
190
|
+
normalized_path: str,
|
|
191
|
+
family: str,
|
|
192
|
+
sensitivity: SecretSensitivity,
|
|
193
|
+
) -> SecretPathMatch:
|
|
194
|
+
return SecretPathMatch(
|
|
195
|
+
family=family,
|
|
196
|
+
path=normalized_path,
|
|
197
|
+
sensitivity=sensitivity,
|
|
198
|
+
reason=_SENSITIVE_PATH_REASONS[family],
|
|
199
|
+
requested_path=requested_path,
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def _expand_home(value: str, home_dir: Path | None) -> str:
|
|
204
|
+
if value == "~":
|
|
205
|
+
return str(home_dir or Path.home())
|
|
206
|
+
if value.startswith("~/") or value.startswith("~\\"):
|
|
207
|
+
base = home_dir or Path.home()
|
|
208
|
+
return str(base / value[2:])
|
|
209
|
+
return value
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
def _normalize_path(value: str, cwd: Path | None) -> str:
|
|
213
|
+
if os.path.isabs(value):
|
|
214
|
+
return os.path.normpath(value)
|
|
215
|
+
if cwd is not None:
|
|
216
|
+
return os.path.normpath(os.path.join(str(cwd), value))
|
|
217
|
+
return os.path.normpath(value)
|
|
@@ -3962,6 +3962,46 @@ args = ["-lc", "echo hi"]
|
|
|
3962
3962
|
assert any("native hooks are disabled" in warning for warning in output["warnings"])
|
|
3963
3963
|
assert any("managed Codex hooks are missing" in warning for warning in output["warnings"])
|
|
3964
3964
|
|
|
3965
|
+
def test_guard_doctor_reports_runtime_detector_registry_state(self, tmp_path, monkeypatch, capsys):
|
|
3966
|
+
home_dir = tmp_path / "home"
|
|
3967
|
+
guard_home = tmp_path / "guard-home"
|
|
3968
|
+
_write_text(
|
|
3969
|
+
guard_home / "config.toml",
|
|
3970
|
+
"\n".join(
|
|
3971
|
+
[
|
|
3972
|
+
"runtime_detector_registry = true",
|
|
3973
|
+
"runtime_detector_timeout_ms = 75",
|
|
3974
|
+
'runtime_detector_disabled_ids = ["secret.local"]',
|
|
3975
|
+
]
|
|
3976
|
+
)
|
|
3977
|
+
+ "\n",
|
|
3978
|
+
)
|
|
3979
|
+
monkeypatch.setattr("codex_plugin_scanner.guard.adapters.codex._command_available", lambda command: True)
|
|
3980
|
+
|
|
3981
|
+
rc = main(["guard", "doctor", "codex", "--home", str(home_dir), "--guard-home", str(guard_home), "--json"])
|
|
3982
|
+
output = json.loads(capsys.readouterr().out)
|
|
3983
|
+
|
|
3984
|
+
assert rc == 0
|
|
3985
|
+
assert output["runtime_detector_registry"] == {
|
|
3986
|
+
"enabled": True,
|
|
3987
|
+
"debug_trace": False,
|
|
3988
|
+
"timeout_ms": 75,
|
|
3989
|
+
"disabled_detector_ids": ["secret.local"],
|
|
3990
|
+
}
|
|
3991
|
+
|
|
3992
|
+
def test_guard_doctor_human_output_includes_detector_registry_line(self, tmp_path, monkeypatch, capsys):
|
|
3993
|
+
home_dir = tmp_path / "home"
|
|
3994
|
+
guard_home = tmp_path / "guard-home"
|
|
3995
|
+
_write_text(guard_home / "config.toml", "runtime_detector_registry = true\n")
|
|
3996
|
+
monkeypatch.setattr("codex_plugin_scanner.guard.adapters.codex._command_available", lambda command: True)
|
|
3997
|
+
|
|
3998
|
+
rc = main(["guard", "doctor", "codex", "--home", str(home_dir), "--guard-home", str(guard_home)])
|
|
3999
|
+
output = capsys.readouterr().out
|
|
4000
|
+
|
|
4001
|
+
assert rc == 0
|
|
4002
|
+
assert "Detector registry" in output
|
|
4003
|
+
assert "enabled" in output
|
|
4004
|
+
|
|
3965
4005
|
def test_guard_codex_hook_blocks_shell_file_upload_script(self, tmp_path, capsys):
|
|
3966
4006
|
home_dir = tmp_path / "home"
|
|
3967
4007
|
workspace_dir = tmp_path / "workspace"
|