plugin-scanner 2.0.105__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.105 → plugin_scanner-2.0.106}/PKG-INFO +1 -1
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/pyproject.toml +1 -1
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/pyproject.toml.bak +1 -1
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/risk.py +2 -6
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/runtime/actions.py +32 -2
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/runtime/detectors.py +43 -1
- {plugin_scanner-2.0.105 → 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.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/version.py +1 -1
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_guard_risk.py +60 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_guard_runtime.py +46 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_guard_runtime_actions.py +45 -1
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_guard_runtime_detectors.py +156 -3
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/.clusterfuzzlite/Dockerfile +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/.clusterfuzzlite/build.sh +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/.clusterfuzzlite/project.yaml +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/.clusterfuzzlite/requirements-atheris.txt +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/.dockerignore +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/.github/CODEOWNERS +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/.github/ISSUE_TEMPLATE/bug-report.yml +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/.github/ISSUE_TEMPLATE/feature-request.yml +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/.github/dependabot.yml +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/.github/workflows/ci.yml +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/.github/workflows/codeql.yml +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/.github/workflows/dependabot-uv-lock.yml +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/.github/workflows/fuzz.yml +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/.github/workflows/harness-smoke.yml +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/.github/workflows/publish.yml +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/.github/workflows/scorecard.yml +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/.gitignore +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/.pre-commit-hooks.yaml +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/CONTRIBUTING.md +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/Dockerfile +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/LICENSE +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/README.md +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/SECURITY.md +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/dashboard/index.html +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/dashboard/package.json +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/dashboard/pnpm-lock.yaml +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/dashboard/public/apple-touch-icon.png +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/dashboard/public/brand/Logo_Icon_Dark.png +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/dashboard/public/brand/Logo_Whole.png +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/dashboard/public/favicon-16x16.png +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/dashboard/public/favicon-32x32.png +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/dashboard/public/favicon.ico +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/dashboard/src/app.tsx +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/dashboard/src/approval-center-layout.tsx +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/dashboard/src/approval-center-primitives.tsx +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/dashboard/src/approval-center-utils.ts +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/dashboard/src/fleet-workspace.tsx +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/dashboard/src/guard-api.test.ts +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/dashboard/src/guard-api.ts +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/dashboard/src/guard-demo.ts +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/dashboard/src/guard-types.ts +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/dashboard/src/main.tsx +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/dashboard/src/receipts-workspace.tsx +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/dashboard/src/runtime-overview.tsx +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/dashboard/src/settings-workspace.tsx +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/dashboard/src/styles.css +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/dashboard/src/vite-env.d.ts +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/dashboard/tsconfig.json +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/dashboard/vite.config.ts +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/docker-requirements.txt +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/docs/guard/approval-audit.md +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/docs/guard/architecture.md +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/docs/guard/get-started.md +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/docs/guard/harness-support.md +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/docs/guard/local-vs-cloud.md +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/docs/guard/testing-matrix.md +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/docs/trust/mcp-trust-draft.md +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/docs/trust/plugin-trust-draft.md +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/docs/trust/skill-trust-local.md +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/fuzzers/manifest_fuzzer.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/requirements.txt +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/schemas/plugin-quality.v1.json +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/schemas/scan-result.v1.json +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/schemas/verify-result.v1.json +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/__init__.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/action_runner.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/argparse_utils.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/checks/__init__.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/checks/best_practices.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/checks/claude.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/checks/code_quality.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/checks/ecosystem_common.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/checks/gemini.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/checks/manifest.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/checks/manifest_support.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/checks/marketplace.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/checks/mcp_security.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/checks/opencode.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/checks/operational_security.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/checks/security.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/checks/skill_security.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/cli.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/cli_ui.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/config.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/ecosystems/__init__.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/ecosystems/base.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/ecosystems/claude.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/ecosystems/codex.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/ecosystems/detect.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/ecosystems/gemini.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/ecosystems/opencode.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/ecosystems/registry.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/ecosystems/types.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/github_reporting.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/__init__.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/adapters/__init__.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/adapters/antigravity.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/adapters/base.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/adapters/claude_code.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/adapters/cloud_identity.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/adapters/codex.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/adapters/copilot.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/adapters/cursor.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/adapters/gemini.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/adapters/hermes.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/adapters/mcp_servers.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/adapters/openclaw.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/adapters/openclaw_config.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/adapters/openclaw_support.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/adapters/opencode.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/adapters/opencode_artifacts.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/advisory_model.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/approvals.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/bridge/__init__.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/capabilities.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/cli/__init__.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/cli/approval_commands.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/cli/bootstrap.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/cli/commands.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/cli/connect_flow.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/cli/install_commands.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/cli/product.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/cli/prompt.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/cli/render.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/cli/update_commands.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/codex_config.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/config.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/consumer/__init__.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/consumer/service.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/daemon/__init__.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/daemon/client.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/daemon/manager.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/daemon/server.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/daemon/static/apple-touch-icon.png +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/daemon/static/assets/guard-dashboard.js +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/daemon/static/assets/index.css +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/daemon/static/brand/Logo_Icon_Dark.png +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/daemon/static/brand/Logo_Whole.png +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/daemon/static/favicon-16x16.png +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/daemon/static/favicon-32x32.png +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/daemon/static/favicon.ico +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/daemon/static/index.html +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/edge_events.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/incident.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/launcher.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/mcp_tool_calls.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/models.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/policy/__init__.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/policy/engine.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/protect.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/proxy/__init__.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/proxy/remote.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/proxy/runtime_mcp.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/proxy/stdio.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/receipts/__init__.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/receipts/manager.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/redaction.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/runtime/__init__.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/runtime/decisions.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/runtime/runner.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/runtime/signals.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/runtime/surface_server.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/schemas/__init__.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/schemas/consumer_mode.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/schemas/guard_event_v1.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/schemas/surface_server.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/shims.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/store.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/store_approvals.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/store_connect.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/guard/types.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/integrations/__init__.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/integrations/cisco_mcp_scanner.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/integrations/cisco_skill_scanner.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/lint_fixes.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/marketplace_support.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/models.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/path_support.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/policy.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/quality_artifact.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/repo_detect.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/reporting.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/rules/__init__.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/rules/registry.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/rules/specs.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/scanner.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/submission.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/suppressions.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/trust_domain_scoring.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/trust_helpers.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/trust_mcp_scoring.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/trust_models.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/trust_plugin_scoring.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/trust_scoring.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/trust_skill_scoring.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/trust_specs.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/src/codex_plugin_scanner/verification.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/__init__.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/conftest.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/__init__.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/bad-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/bad-plugin/.mcp.json +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/bad-plugin/secrets.js +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/claude-plugin-good/.claude-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/claude-plugin-good/LICENSE +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/claude-plugin-good/README.md +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/claude-plugin-good/SECURITY.md +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/claude-plugin-good/hooks/hooks.json +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/claude-plugin-good/skills/example/SKILL.md +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/code-quality-bad/evil.js +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/code-quality-bad/inject.js +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/gemini-extension-good/GEMINI.md +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/gemini-extension-good/LICENSE +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/gemini-extension-good/README.md +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/gemini-extension-good/SECURITY.md +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/gemini-extension-good/commands/hello.toml +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/gemini-extension-good/gemini-extension.json +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/good-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/good-plugin/.codexignore +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/good-plugin/LICENSE +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/good-plugin/README.md +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/good-plugin/SECURITY.md +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/good-plugin/assets/icon.svg +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/good-plugin/assets/logo.svg +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/good-plugin/assets/screenshot.svg +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/good-plugin/skills/example/SKILL.md +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/guard-codex-malicious-mcp/.codex/config.toml +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/hermes-plugin-evil/config.yaml +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/hermes-plugin-evil/mcp_servers.json +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/hermes-plugin-evil/skills/security/malicious/SKILL.md +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/SKILL.md +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/references/api-setup.md +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/scripts/deploy.sh +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/hermes-plugin-evil/skills/utils/benign/SKILL.md +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/malformed-json/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/malicious-skill-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/malicious-skill-plugin/.codexignore +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/malicious-skill-plugin/LICENSE +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/malicious-skill-plugin/README.md +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/malicious-skill-plugin/SECURITY.md +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/malicious-skill-plugin/skills/leaky-skill/SKILL.md +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/mcp-canary-server.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/minimal-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/missing-fields/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/mit-license/LICENSE +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/multi-ecosystem-repo/codex-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/multi-ecosystem-repo/codex-plugin/LICENSE +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/multi-ecosystem-repo/codex-plugin/README.md +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/multi-ecosystem-repo/codex-plugin/SECURITY.md +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/multi-ecosystem-repo/gemini-ext/README.md +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/multi-ecosystem-repo/gemini-ext/gemini-extension.json +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/multi-plugin-repo/.agents/plugins/marketplace.json +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/.codexignore +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/LICENSE +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/README.md +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/SECURITY.md +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/skills/example/SKILL.md +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/multi-plugin-repo/plugins/beta-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/multi-plugin-repo/plugins/beta-plugin/skills/example/SKILL.md +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/no-version/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/opencode-good/.opencode/commands/hello.md +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/opencode-good/.opencode/plugins/example.ts +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/opencode-good/LICENSE +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/opencode-good/README.md +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/opencode-good/SECURITY.md +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/opencode-good/opencode.jsonc +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/skills-missing-dir/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/skills-no-frontmatter/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/skills-no-frontmatter/skills/bad-skill/SKILL.md +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/with-marketplace/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/with-marketplace/marketplace-broken.json +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/fixtures/with-marketplace/marketplace.json +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test-trust-scoring.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test-trust-specs.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_action_runner.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_best_practices.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_cisco_install_surfaces.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_cli.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_code_quality.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_config.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_coverage_remaining.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_ecosystems.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_edge_cases.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_final_coverage.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_guard_approvals.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_guard_bootstrap.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_guard_capabilities.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_guard_claude_adapter.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_guard_cli.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_guard_codex_e2e.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_guard_codex_install.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_guard_codex_proxy.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_guard_config_paths.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_guard_connect_flow.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_guard_consumer_mode.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_guard_copilot_adapter.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_guard_copilot_proxy.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_guard_daemon_manager.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_guard_event_schema_v1.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_guard_events.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_guard_launch_env.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_guard_opencode_proxy.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_guard_product_flow.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_guard_protect.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_guard_render.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_guard_runtime_action_harnesses.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_guard_runtime_decisions.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_guard_runtime_signals.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_guard_store_migrations.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_guard_surface_server.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_guard_verdicts.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_hermes_adapter.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_integration.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_lint_fixes.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_live_cisco_smoke.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_manifest.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_marketplace.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_mcp_security.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_openclaw_adapter.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_operational_security.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_policy.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_quality_artifact.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_rule_registry.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_scanner.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_schema_contracts.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_security.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_security_ops.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_skill_security.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_submission.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_trust_scoring.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_trust_specs.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_verification.py +0 -0
- {plugin_scanner-2.0.105 → plugin_scanner-2.0.106}/tests/test_versioning.py +0 -0
- {plugin_scanner-2.0.105 → 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"
|
|
@@ -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.105 → 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,
|
|
@@ -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)
|
|
@@ -24,6 +24,7 @@ from codex_plugin_scanner.guard.risk import (
|
|
|
24
24
|
artifact_risk_signals_typed,
|
|
25
25
|
artifact_risk_signals_v2,
|
|
26
26
|
artifact_risk_summary,
|
|
27
|
+
classify_secret_paths,
|
|
27
28
|
detect_encoded_command,
|
|
28
29
|
detect_guard_bypass,
|
|
29
30
|
detect_staged_download,
|
|
@@ -43,6 +44,7 @@ from codex_plugin_scanner.guard.runtime.secret_file_requests import (
|
|
|
43
44
|
is_explicitly_benign_tool_action_request,
|
|
44
45
|
is_file_read_tool_name,
|
|
45
46
|
)
|
|
47
|
+
from codex_plugin_scanner.guard.runtime.secret_sensitivity import SecretPathMatch, classify_secret_path
|
|
46
48
|
from codex_plugin_scanner.guard.store import GuardStore
|
|
47
49
|
|
|
48
50
|
|
|
@@ -109,6 +111,16 @@ def test_artifact_risk_signals_legacy_strings_remain_stable():
|
|
|
109
111
|
)
|
|
110
112
|
|
|
111
113
|
|
|
114
|
+
def test_classify_secret_paths_preserves_legacy_labels():
|
|
115
|
+
classes = classify_secret_paths(".pypirc ~/.aws/" + "credentials ~/.docker/" + "config.json")
|
|
116
|
+
|
|
117
|
+
assert classes == {
|
|
118
|
+
"python package credentials",
|
|
119
|
+
"aws shared credentials",
|
|
120
|
+
"docker credentials",
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
|
|
112
124
|
def test_artifact_risk_signals_v2_adapts_existing_signal_metadata():
|
|
113
125
|
artifact = GuardArtifact(
|
|
114
126
|
artifact_id="codex:project:secret_probe",
|
|
@@ -346,6 +358,32 @@ def test_secret_file_path_classifier_stays_precise(tmp_path):
|
|
|
346
358
|
assert classify_sensitive_path(".envrc") is None
|
|
347
359
|
|
|
348
360
|
|
|
361
|
+
@pytest.mark.parametrize(
|
|
362
|
+
("path", "family"),
|
|
363
|
+
[
|
|
364
|
+
(".env", "local .env file"),
|
|
365
|
+
(".npmrc", "npm registry credentials"),
|
|
366
|
+
(".pypirc", "Python package credentials"),
|
|
367
|
+
("~/.aws/" + "credentials", "AWS shared credentials file"),
|
|
368
|
+
("~/.ssh/id_rsa", "SSH private key"),
|
|
369
|
+
("~/.ssh/id_ed25519", "SSH private key"),
|
|
370
|
+
("~/.gnupg/private-keys-v1.d/example.key", "GnuPG key material"),
|
|
371
|
+
("~/.docker/" + "config.json", "Docker client config"),
|
|
372
|
+
("~/.kube/config", "Kubernetes config"),
|
|
373
|
+
(".terraform.tfvars", "Terraform variable secrets"),
|
|
374
|
+
],
|
|
375
|
+
)
|
|
376
|
+
def test_secret_sensitivity_module_classifies_planned_secret_path_families(tmp_path, path, family):
|
|
377
|
+
match = classify_secret_path(path, home_dir=tmp_path)
|
|
378
|
+
|
|
379
|
+
assert isinstance(match, SecretPathMatch)
|
|
380
|
+
assert match.family == family
|
|
381
|
+
assert match.path_class == family
|
|
382
|
+
assert match.path == match.normalized_path
|
|
383
|
+
assert match.sensitivity in {"high", "critical"}
|
|
384
|
+
assert match.reason
|
|
385
|
+
|
|
386
|
+
|
|
349
387
|
def test_file_read_request_classifier_is_argument_aware(tmp_path):
|
|
350
388
|
env_request = extract_sensitive_file_read_request("read_file", {"path": ".env.local"})
|
|
351
389
|
claude_request = extract_sensitive_file_read_request("Read", {"file_path": "~/.ssh/config"}, home_dir=tmp_path)
|
|
@@ -365,6 +403,28 @@ def test_file_read_request_classifier_is_argument_aware(tmp_path):
|
|
|
365
403
|
assert extract_sensitive_file_read_request("write_file", {"path": ".env"}) is None
|
|
366
404
|
|
|
367
405
|
|
|
406
|
+
@pytest.mark.parametrize(
|
|
407
|
+
("path", "family"),
|
|
408
|
+
[
|
|
409
|
+
(".env", "local .env file"),
|
|
410
|
+
(".npmrc", "npm registry credentials"),
|
|
411
|
+
(".pypirc", "Python package credentials"),
|
|
412
|
+
("~/.aws/" + "credentials", "AWS shared credentials file"),
|
|
413
|
+
("~/.ssh/id_rsa", "SSH private key"),
|
|
414
|
+
("~/.ssh/id_ed25519", "SSH private key"),
|
|
415
|
+
("~/.gnupg/private-keys-v1.d/example.key", "GnuPG key material"),
|
|
416
|
+
("~/.docker/" + "config.json", "Docker client config"),
|
|
417
|
+
("~/.kube/config", "Kubernetes config"),
|
|
418
|
+
(".terraform.tfvars", "Terraform variable secrets"),
|
|
419
|
+
],
|
|
420
|
+
)
|
|
421
|
+
def test_file_read_request_classifier_covers_planned_secret_paths(tmp_path, path, family):
|
|
422
|
+
request = extract_sensitive_file_read_request("Read", {"file_path": path}, home_dir=tmp_path)
|
|
423
|
+
|
|
424
|
+
assert request is not None
|
|
425
|
+
assert request.path_match.family == family
|
|
426
|
+
|
|
427
|
+
|
|
368
428
|
def test_file_read_request_artifact_hash_is_exact_to_tool_and_path():
|
|
369
429
|
first_request = extract_sensitive_file_read_request("read_file", {"path": ".env"})
|
|
370
430
|
same_request = extract_sensitive_file_read_request("read_file", {"path": ".env"})
|
|
@@ -2400,6 +2400,52 @@ clearer UX and an implementation plan with technical references.
|
|
|
2400
2400
|
assert output["policy_action"] == "require-reapproval"
|
|
2401
2401
|
assert output["path_summary"] == str(home_dir / ".env")
|
|
2402
2402
|
|
|
2403
|
+
@pytest.mark.parametrize(
|
|
2404
|
+
"path",
|
|
2405
|
+
[
|
|
2406
|
+
".env",
|
|
2407
|
+
".npmrc",
|
|
2408
|
+
".pypirc",
|
|
2409
|
+
"~/.aws/" + "credentials",
|
|
2410
|
+
"~/.ssh/id_rsa",
|
|
2411
|
+
"~/.ssh/id_ed25519",
|
|
2412
|
+
"~/.gnupg/private-keys-v1.d/example.key",
|
|
2413
|
+
"~/.docker/" + "config.json",
|
|
2414
|
+
"~/.kube/config",
|
|
2415
|
+
".terraform.tfvars",
|
|
2416
|
+
],
|
|
2417
|
+
)
|
|
2418
|
+
def test_guard_hook_asks_for_planned_secret_file_reads(self, tmp_path, capsys, monkeypatch, path):
|
|
2419
|
+
home_dir = tmp_path / "home"
|
|
2420
|
+
workspace_dir = tmp_path / "workspace"
|
|
2421
|
+
_build_guard_fixture(home_dir, workspace_dir)
|
|
2422
|
+
event = {
|
|
2423
|
+
"tool_name": "read_file",
|
|
2424
|
+
"tool_input": {"path": path},
|
|
2425
|
+
"source_scope": "project",
|
|
2426
|
+
}
|
|
2427
|
+
monkeypatch.setattr(sys, "stdin", io.StringIO(json.dumps(event)))
|
|
2428
|
+
monkeypatch.setattr(guard_commands_module, "ensure_guard_daemon", lambda _guard_home: "http://127.0.0.1:4455")
|
|
2429
|
+
|
|
2430
|
+
rc = main(
|
|
2431
|
+
[
|
|
2432
|
+
"guard",
|
|
2433
|
+
"hook",
|
|
2434
|
+
"--home",
|
|
2435
|
+
str(home_dir),
|
|
2436
|
+
"--workspace",
|
|
2437
|
+
str(workspace_dir),
|
|
2438
|
+
"--harness",
|
|
2439
|
+
"copilot",
|
|
2440
|
+
"--json",
|
|
2441
|
+
]
|
|
2442
|
+
)
|
|
2443
|
+
output = json.loads(capsys.readouterr().out)
|
|
2444
|
+
|
|
2445
|
+
assert rc == 1
|
|
2446
|
+
assert output["artifact_type"] == "file_read_request"
|
|
2447
|
+
assert output["policy_action"] == "require-reapproval"
|
|
2448
|
+
|
|
2403
2449
|
|
|
2404
2450
|
def test_guard_hook_emits_copilot_native_ask_response(tmp_path, capsys, monkeypatch):
|
|
2405
2451
|
home_dir = tmp_path / "home"
|