plugin-scanner 2.0.109__tar.gz → 2.0.111__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.109 → plugin_scanner-2.0.111}/PKG-INFO +1 -1
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/pyproject.toml +1 -1
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/pyproject.toml.bak +1 -1
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/cli/commands.py +386 -24
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/cli/render.py +3 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/consumer/service.py +9 -1
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/runtime/secret_sensitivity.py +25 -2
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/version.py +1 -1
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_guard_render.py +23 -5
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_guard_risk.py +20 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_guard_runtime.py +559 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/.clusterfuzzlite/Dockerfile +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/.clusterfuzzlite/build.sh +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/.clusterfuzzlite/project.yaml +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/.clusterfuzzlite/requirements-atheris.txt +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/.dockerignore +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/.github/CODEOWNERS +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/.github/ISSUE_TEMPLATE/bug-report.yml +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/.github/ISSUE_TEMPLATE/feature-request.yml +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/.github/dependabot.yml +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/.github/workflows/ci.yml +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/.github/workflows/codeql.yml +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/.github/workflows/dependabot-uv-lock.yml +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/.github/workflows/fuzz.yml +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/.github/workflows/harness-smoke.yml +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/.github/workflows/publish.yml +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/.github/workflows/scorecard.yml +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/.gitignore +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/.pre-commit-hooks.yaml +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/CONTRIBUTING.md +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/Dockerfile +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/LICENSE +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/README.md +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/SECURITY.md +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/dashboard/index.html +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/dashboard/package.json +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/dashboard/pnpm-lock.yaml +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/dashboard/public/apple-touch-icon.png +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/dashboard/public/brand/Logo_Icon_Dark.png +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/dashboard/public/brand/Logo_Whole.png +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/dashboard/public/favicon-16x16.png +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/dashboard/public/favicon-32x32.png +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/dashboard/public/favicon.ico +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/dashboard/src/app.tsx +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/dashboard/src/approval-center-layout.tsx +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/dashboard/src/approval-center-primitives.tsx +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/dashboard/src/approval-center-utils.ts +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/dashboard/src/fleet-workspace.tsx +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/dashboard/src/guard-api.test.ts +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/dashboard/src/guard-api.ts +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/dashboard/src/guard-demo.ts +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/dashboard/src/guard-types.ts +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/dashboard/src/main.tsx +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/dashboard/src/receipts-workspace.tsx +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/dashboard/src/runtime-overview.tsx +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/dashboard/src/settings-workspace.tsx +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/dashboard/src/styles.css +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/dashboard/src/vite-env.d.ts +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/dashboard/tsconfig.json +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/dashboard/vite.config.ts +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/docker-requirements.txt +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/docs/guard/approval-audit.md +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/docs/guard/architecture.md +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/docs/guard/get-started.md +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/docs/guard/harness-support.md +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/docs/guard/local-vs-cloud.md +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/docs/guard/testing-matrix.md +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/docs/trust/mcp-trust-draft.md +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/docs/trust/plugin-trust-draft.md +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/docs/trust/skill-trust-local.md +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/fuzzers/manifest_fuzzer.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/requirements.txt +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/schemas/plugin-quality.v1.json +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/schemas/scan-result.v1.json +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/schemas/verify-result.v1.json +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/__init__.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/action_runner.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/argparse_utils.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/checks/__init__.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/checks/best_practices.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/checks/claude.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/checks/code_quality.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/checks/ecosystem_common.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/checks/gemini.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/checks/manifest.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/checks/manifest_support.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/checks/marketplace.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/checks/mcp_security.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/checks/opencode.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/checks/operational_security.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/checks/security.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/checks/skill_security.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/cli.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/cli_ui.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/config.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/ecosystems/__init__.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/ecosystems/base.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/ecosystems/claude.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/ecosystems/codex.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/ecosystems/detect.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/ecosystems/gemini.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/ecosystems/opencode.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/ecosystems/registry.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/ecosystems/types.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/github_reporting.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/__init__.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/adapters/__init__.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/adapters/antigravity.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/adapters/base.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/adapters/claude_code.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/adapters/cloud_identity.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/adapters/codex.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/adapters/copilot.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/adapters/cursor.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/adapters/gemini.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/adapters/hermes.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/adapters/mcp_servers.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/adapters/openclaw.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/adapters/openclaw_config.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/adapters/openclaw_support.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/adapters/opencode.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/adapters/opencode_artifacts.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/advisory_model.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/approvals.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/bridge/__init__.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/capabilities.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/cli/__init__.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/cli/approval_commands.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/cli/bootstrap.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/cli/connect_flow.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/cli/install_commands.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/cli/product.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/cli/prompt.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/cli/update_commands.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/codex_config.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/config.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/consumer/__init__.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/daemon/__init__.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/daemon/client.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/daemon/manager.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/daemon/server.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/daemon/static/apple-touch-icon.png +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/daemon/static/assets/guard-dashboard.js +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/daemon/static/assets/index.css +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/daemon/static/brand/Logo_Icon_Dark.png +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/daemon/static/brand/Logo_Whole.png +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/daemon/static/favicon-16x16.png +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/daemon/static/favicon-32x32.png +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/daemon/static/favicon.ico +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/daemon/static/index.html +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/edge_events.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/incident.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/launcher.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/mcp_tool_calls.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/models.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/policy/__init__.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/policy/engine.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/protect.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/proxy/__init__.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/proxy/remote.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/proxy/runtime_mcp.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/proxy/stdio.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/receipts/__init__.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/receipts/manager.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/redaction.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/risk.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/runtime/__init__.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/runtime/actions.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/runtime/decisions.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/runtime/detectors.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/runtime/runner.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/runtime/secret_file_requests.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/runtime/signals.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/runtime/surface_server.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/schemas/__init__.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/schemas/consumer_mode.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/schemas/guard_event_v1.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/schemas/surface_server.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/shims.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/store.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/store_approvals.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/store_connect.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/types.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/integrations/__init__.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/integrations/cisco_mcp_scanner.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/integrations/cisco_skill_scanner.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/lint_fixes.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/marketplace_support.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/models.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/path_support.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/policy.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/quality_artifact.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/repo_detect.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/reporting.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/rules/__init__.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/rules/registry.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/rules/specs.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/scanner.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/submission.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/suppressions.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/trust_domain_scoring.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/trust_helpers.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/trust_mcp_scoring.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/trust_models.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/trust_plugin_scoring.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/trust_scoring.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/trust_skill_scoring.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/trust_specs.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/verification.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/__init__.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/conftest.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/__init__.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/bad-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/bad-plugin/.mcp.json +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/bad-plugin/secrets.js +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/claude-plugin-good/.claude-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/claude-plugin-good/LICENSE +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/claude-plugin-good/README.md +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/claude-plugin-good/SECURITY.md +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/claude-plugin-good/hooks/hooks.json +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/claude-plugin-good/skills/example/SKILL.md +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/code-quality-bad/evil.js +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/code-quality-bad/inject.js +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/gemini-extension-good/GEMINI.md +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/gemini-extension-good/LICENSE +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/gemini-extension-good/README.md +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/gemini-extension-good/SECURITY.md +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/gemini-extension-good/commands/hello.toml +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/gemini-extension-good/gemini-extension.json +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/good-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/good-plugin/.codexignore +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/good-plugin/LICENSE +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/good-plugin/README.md +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/good-plugin/SECURITY.md +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/good-plugin/assets/icon.svg +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/good-plugin/assets/logo.svg +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/good-plugin/assets/screenshot.svg +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/good-plugin/skills/example/SKILL.md +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/guard-codex-malicious-mcp/.codex/config.toml +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/hermes-plugin-evil/config.yaml +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/hermes-plugin-evil/mcp_servers.json +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/hermes-plugin-evil/skills/security/malicious/SKILL.md +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/SKILL.md +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/references/api-setup.md +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/scripts/deploy.sh +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/hermes-plugin-evil/skills/utils/benign/SKILL.md +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/malformed-json/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/malicious-skill-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/malicious-skill-plugin/.codexignore +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/malicious-skill-plugin/LICENSE +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/malicious-skill-plugin/README.md +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/malicious-skill-plugin/SECURITY.md +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/malicious-skill-plugin/skills/leaky-skill/SKILL.md +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/mcp-canary-server.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/minimal-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/missing-fields/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/mit-license/LICENSE +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/multi-ecosystem-repo/codex-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/multi-ecosystem-repo/codex-plugin/LICENSE +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/multi-ecosystem-repo/codex-plugin/README.md +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/multi-ecosystem-repo/codex-plugin/SECURITY.md +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/multi-ecosystem-repo/gemini-ext/README.md +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/multi-ecosystem-repo/gemini-ext/gemini-extension.json +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/multi-plugin-repo/.agents/plugins/marketplace.json +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/.codexignore +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/LICENSE +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/README.md +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/SECURITY.md +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/skills/example/SKILL.md +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/multi-plugin-repo/plugins/beta-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/multi-plugin-repo/plugins/beta-plugin/skills/example/SKILL.md +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/no-version/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/opencode-good/.opencode/commands/hello.md +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/opencode-good/.opencode/plugins/example.ts +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/opencode-good/LICENSE +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/opencode-good/README.md +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/opencode-good/SECURITY.md +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/opencode-good/opencode.jsonc +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/skills-missing-dir/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/skills-no-frontmatter/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/skills-no-frontmatter/skills/bad-skill/SKILL.md +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/with-marketplace/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/with-marketplace/marketplace-broken.json +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/fixtures/with-marketplace/marketplace.json +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test-trust-scoring.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test-trust-specs.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_action_runner.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_best_practices.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_cisco_install_surfaces.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_cli.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_code_quality.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_config.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_coverage_remaining.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_ecosystems.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_edge_cases.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_final_coverage.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_guard_access_graph.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_guard_approvals.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_guard_bootstrap.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_guard_capabilities.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_guard_claude_adapter.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_guard_cli.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_guard_codex_e2e.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_guard_codex_install.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_guard_codex_proxy.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_guard_config_paths.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_guard_connect_flow.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_guard_consumer_mode.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_guard_copilot_adapter.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_guard_copilot_proxy.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_guard_daemon_manager.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_guard_event_schema_v1.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_guard_events.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_guard_launch_env.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_guard_opencode_proxy.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_guard_product_flow.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_guard_protect.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_guard_runtime_action_harnesses.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_guard_runtime_actions.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_guard_runtime_decisions.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_guard_runtime_detectors.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_guard_runtime_signals.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_guard_store_migrations.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_guard_surface_server.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_guard_verdicts.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_hermes_adapter.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_integration.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_lint_fixes.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_live_cisco_smoke.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_manifest.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_marketplace.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_mcp_security.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_openclaw_adapter.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_operational_security.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_policy.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_quality_artifact.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_rule_registry.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_scanner.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_schema_contracts.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_security.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_security_ops.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_skill_security.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_submission.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_trust_scoring.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_trust_specs.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_verification.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/tests/test_versioning.py +0 -0
- {plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/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.111
|
|
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.111"
|
|
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.111"
|
|
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.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/cli/commands.py
RENAMED
|
@@ -97,7 +97,12 @@ from ..runtime.secret_file_requests import (
|
|
|
97
97
|
extract_sensitive_tool_action_request,
|
|
98
98
|
is_explicitly_benign_tool_action_request,
|
|
99
99
|
)
|
|
100
|
-
from ..runtime.secret_sensitivity import
|
|
100
|
+
from ..runtime.secret_sensitivity import (
|
|
101
|
+
SecretContentMatch,
|
|
102
|
+
SecretPathMatch,
|
|
103
|
+
classify_secret_content,
|
|
104
|
+
classify_secret_path,
|
|
105
|
+
)
|
|
101
106
|
from ..runtime.surface_server import GuardSurfaceRuntime
|
|
102
107
|
from ..store import GuardStore
|
|
103
108
|
from .approval_commands import add_approval_parser, run_approval_command
|
|
@@ -2999,16 +3004,47 @@ def _runtime_stored_policy_action(
|
|
|
2999
3004
|
def _runtime_artifact_policy_action(config: GuardConfig, artifact: GuardArtifact, harness: str) -> str:
|
|
3000
3005
|
if _prompt_requires_hard_block(artifact):
|
|
3001
3006
|
return "block"
|
|
3002
|
-
|
|
3003
|
-
|
|
3004
|
-
|
|
3005
|
-
|
|
3007
|
+
canonical_harness = _canonical_harness_name(harness)
|
|
3008
|
+
risk_classes = _runtime_artifact_risk_classes(artifact)
|
|
3009
|
+
has_configured_risk_action = any(
|
|
3010
|
+
_resolve_configured_risk_action(config, risk_class, harness=canonical_harness) for risk_class in risk_classes
|
|
3011
|
+
)
|
|
3012
|
+
if has_configured_risk_action:
|
|
3013
|
+
risk_actions = [
|
|
3014
|
+
_resolve_configured_risk_action(config, risk_class, harness=canonical_harness)
|
|
3015
|
+
or resolve_risk_action(config, risk_class, harness=canonical_harness)
|
|
3016
|
+
for risk_class in risk_classes
|
|
3017
|
+
]
|
|
3018
|
+
resolved_actions = [action for action in risk_actions if action in VALID_GUARD_ACTIONS]
|
|
3019
|
+
if resolved_actions:
|
|
3020
|
+
return max(resolved_actions, key=_guard_action_severity)
|
|
3021
|
+
guard_default_action = _runtime_artifact_guard_default_action(artifact)
|
|
3022
|
+
if guard_default_action is not None:
|
|
3023
|
+
return guard_default_action
|
|
3024
|
+
risk_actions = [resolve_risk_action(config, risk_class, harness=canonical_harness) for risk_class in risk_classes]
|
|
3006
3025
|
resolved_actions = [action for action in risk_actions if action in VALID_GUARD_ACTIONS]
|
|
3007
3026
|
if resolved_actions:
|
|
3008
3027
|
return max(resolved_actions, key=_guard_action_severity)
|
|
3009
3028
|
return SAFE_CHANGED_HASH_ACTION
|
|
3010
3029
|
|
|
3011
3030
|
|
|
3031
|
+
def _resolve_configured_risk_action(config: GuardConfig, risk_class: str, *, harness: str) -> str | None:
|
|
3032
|
+
if config.harness_risk_actions is not None:
|
|
3033
|
+
harness_actions = config.harness_risk_actions.get(harness)
|
|
3034
|
+
if harness_actions is not None and risk_class in harness_actions:
|
|
3035
|
+
return harness_actions[risk_class]
|
|
3036
|
+
if config.risk_actions is not None and risk_class in config.risk_actions:
|
|
3037
|
+
return config.risk_actions[risk_class]
|
|
3038
|
+
return None
|
|
3039
|
+
|
|
3040
|
+
|
|
3041
|
+
def _runtime_artifact_guard_default_action(artifact: GuardArtifact) -> str | None:
|
|
3042
|
+
value = artifact.metadata.get("guard_default_action")
|
|
3043
|
+
if value in VALID_GUARD_ACTIONS:
|
|
3044
|
+
return str(value)
|
|
3045
|
+
return None
|
|
3046
|
+
|
|
3047
|
+
|
|
3012
3048
|
def _guard_action_severity(action: str) -> int:
|
|
3013
3049
|
return {
|
|
3014
3050
|
"allow": 0,
|
|
@@ -3205,6 +3241,12 @@ def _runtime_artifact_native_reason(artifact: GuardArtifact, response_payload: d
|
|
|
3205
3241
|
trimmed_summary = risk_summary.strip()
|
|
3206
3242
|
if len(trimmed_summary) > 180:
|
|
3207
3243
|
trimmed_summary = f"{trimmed_summary[:177].rstrip()}..."
|
|
3244
|
+
action_class = artifact.metadata.get("action_class")
|
|
3245
|
+
if (
|
|
3246
|
+
action_class == "credential exfiltration shell command"
|
|
3247
|
+
and "credential-looking output" not in trimmed_summary.lower()
|
|
3248
|
+
):
|
|
3249
|
+
trimmed_summary = f"{trimmed_summary} Guard also detected credential-looking output."
|
|
3208
3250
|
return f"HOL Guard flagged this request: {trimmed_summary}"
|
|
3209
3251
|
return "HOL Guard flagged this request for review."
|
|
3210
3252
|
|
|
@@ -3985,13 +4027,19 @@ def _codex_post_tool_output_artifact(
|
|
|
3985
4027
|
cwd: Path | None,
|
|
3986
4028
|
) -> GuardArtifact | None:
|
|
3987
4029
|
response_text = _collect_codex_tool_response_text(payload.get("tool_response"))
|
|
3988
|
-
content_matches = classify_secret_content(response_text)
|
|
3989
|
-
if not content_matches:
|
|
3990
|
-
return None
|
|
3991
4030
|
tool_name = _coalesce_string(payload.get("tool_name"), "Bash")
|
|
3992
4031
|
command_text = _codex_post_tool_command_text(payload)
|
|
3993
4032
|
if not command_text:
|
|
3994
4033
|
command_text = tool_name
|
|
4034
|
+
local_source_matches = _codex_sensitive_local_source_matches(command_text, cwd=cwd)
|
|
4035
|
+
references_local_content = bool(local_source_matches) or _codex_command_may_read_local_content(
|
|
4036
|
+
command_text, cwd=cwd
|
|
4037
|
+
)
|
|
4038
|
+
content_matches = classify_secret_content(response_text)
|
|
4039
|
+
if not content_matches and references_local_content:
|
|
4040
|
+
content_matches = classify_secret_content(response_text, suppress_samples=False)
|
|
4041
|
+
if not content_matches:
|
|
4042
|
+
return None
|
|
3995
4043
|
if _codex_source_inspection_can_skip_secret_output(
|
|
3996
4044
|
command_text=command_text,
|
|
3997
4045
|
response_text=response_text,
|
|
@@ -4009,6 +4057,38 @@ def _codex_post_tool_output_artifact(
|
|
|
4009
4057
|
sort_keys=True,
|
|
4010
4058
|
).encode("utf-8")
|
|
4011
4059
|
).hexdigest()
|
|
4060
|
+
local_secret_source = _codex_local_secret_source_label(
|
|
4061
|
+
local_source_matches,
|
|
4062
|
+
command_text=command_text,
|
|
4063
|
+
)
|
|
4064
|
+
runtime_default_action = "require-reapproval" if references_local_content else "warn"
|
|
4065
|
+
runtime_request_signals = ["tool output contains credential-looking material"]
|
|
4066
|
+
if references_local_content:
|
|
4067
|
+
source_signal = "command references local secrets"
|
|
4068
|
+
if local_secret_source is not None:
|
|
4069
|
+
source_signal = f"command references local secrets from {local_secret_source}"
|
|
4070
|
+
runtime_request_signals.append(source_signal)
|
|
4071
|
+
request_summary = _codex_tool_output_request_summary(
|
|
4072
|
+
tool_name=tool_name,
|
|
4073
|
+
command_text=command_text,
|
|
4074
|
+
local_secret_source=local_secret_source,
|
|
4075
|
+
)
|
|
4076
|
+
runtime_request_summary = _codex_tool_output_runtime_summary(local_secret_source)
|
|
4077
|
+
metadata: dict[str, object] = {
|
|
4078
|
+
"tool_name": tool_name,
|
|
4079
|
+
"command_text": command_text,
|
|
4080
|
+
"action_class": "credential exfiltration shell command",
|
|
4081
|
+
"guard_default_action": runtime_default_action,
|
|
4082
|
+
"request_summary": request_summary,
|
|
4083
|
+
"runtime_request_signals": runtime_request_signals,
|
|
4084
|
+
"runtime_request_summary": runtime_request_summary,
|
|
4085
|
+
"runtime_request_reason": (
|
|
4086
|
+
"Guard inspects supported Codex tool output before Codex uses it, so accidental secret reads can be "
|
|
4087
|
+
"stopped even when the filename was not obviously sensitive."
|
|
4088
|
+
),
|
|
4089
|
+
}
|
|
4090
|
+
if local_secret_source is not None:
|
|
4091
|
+
metadata["secret_source_family"] = local_secret_source
|
|
4012
4092
|
return GuardArtifact(
|
|
4013
4093
|
artifact_id=f"codex:{source_scope}:tool-output:{fingerprint}",
|
|
4014
4094
|
name=f"{tool_name} credential-looking output",
|
|
@@ -4016,25 +4096,307 @@ def _codex_post_tool_output_artifact(
|
|
|
4016
4096
|
artifact_type="tool_action_request",
|
|
4017
4097
|
source_scope=source_scope,
|
|
4018
4098
|
config_path=config_path,
|
|
4019
|
-
metadata=
|
|
4020
|
-
|
|
4021
|
-
|
|
4022
|
-
|
|
4023
|
-
|
|
4024
|
-
|
|
4025
|
-
|
|
4026
|
-
|
|
4027
|
-
|
|
4028
|
-
|
|
4029
|
-
|
|
4030
|
-
|
|
4031
|
-
|
|
4032
|
-
|
|
4033
|
-
|
|
4034
|
-
|
|
4099
|
+
metadata=metadata,
|
|
4100
|
+
)
|
|
4101
|
+
|
|
4102
|
+
|
|
4103
|
+
def _codex_command_references_sensitive_local_source(command_text: str, *, cwd: Path | None) -> bool:
|
|
4104
|
+
return bool(_codex_sensitive_local_source_matches(command_text, cwd=cwd))
|
|
4105
|
+
|
|
4106
|
+
|
|
4107
|
+
def _codex_sensitive_local_source_matches(command_text: str, *, cwd: Path | None) -> list[SecretPathMatch]:
|
|
4108
|
+
matches = _codex_sensitive_path_matches_in_text(command_text, cwd=cwd)
|
|
4109
|
+
try:
|
|
4110
|
+
parts = shlex.split(command_text)
|
|
4111
|
+
except ValueError:
|
|
4112
|
+
return matches
|
|
4113
|
+
for part in parts:
|
|
4114
|
+
stripped = part.strip()
|
|
4115
|
+
if not stripped or stripped.startswith("-") or _codex_token_is_url(stripped):
|
|
4116
|
+
continue
|
|
4117
|
+
path_match = classify_secret_path(stripped, cwd=cwd)
|
|
4118
|
+
if path_match is not None:
|
|
4119
|
+
matches.append(path_match)
|
|
4120
|
+
return _dedupe_codex_secret_path_matches(matches)
|
|
4121
|
+
|
|
4122
|
+
|
|
4123
|
+
def _dedupe_codex_secret_path_matches(matches: list[SecretPathMatch]) -> list[SecretPathMatch]:
|
|
4124
|
+
deduped: list[SecretPathMatch] = []
|
|
4125
|
+
seen: set[tuple[str, str]] = set()
|
|
4126
|
+
for match in matches:
|
|
4127
|
+
key = (match.family, match.requested_path or match.path)
|
|
4128
|
+
if key in seen:
|
|
4129
|
+
continue
|
|
4130
|
+
seen.add(key)
|
|
4131
|
+
deduped.append(match)
|
|
4132
|
+
return deduped
|
|
4133
|
+
|
|
4134
|
+
|
|
4135
|
+
def _codex_token_is_url(token: str) -> bool:
|
|
4136
|
+
parsed = urllib.parse.urlparse(token)
|
|
4137
|
+
return bool(parsed.scheme and parsed.netloc)
|
|
4138
|
+
|
|
4139
|
+
|
|
4140
|
+
def _codex_text_contains_sensitive_path_token(text: str, *, cwd: Path | None) -> bool:
|
|
4141
|
+
return bool(_codex_sensitive_path_matches_in_text(text, cwd=cwd))
|
|
4142
|
+
|
|
4143
|
+
|
|
4144
|
+
def _codex_sensitive_path_matches_in_text(text: str, *, cwd: Path | None) -> list[SecretPathMatch]:
|
|
4145
|
+
matches: list[SecretPathMatch] = []
|
|
4146
|
+
for match in _PROMPT_PATH_TOKEN_PATTERN.finditer(text):
|
|
4147
|
+
if _codex_path_token_is_url_path(text, match.start()):
|
|
4148
|
+
continue
|
|
4149
|
+
path_match = classify_secret_path(match.group(0), cwd=cwd)
|
|
4150
|
+
if path_match is not None:
|
|
4151
|
+
matches.append(path_match)
|
|
4152
|
+
return matches
|
|
4153
|
+
|
|
4154
|
+
|
|
4155
|
+
def _codex_path_token_is_url_path(text: str, start: int) -> bool:
|
|
4156
|
+
prefix = text[:start].lower()
|
|
4157
|
+
last_separator = max(prefix.rfind(separator) for separator in " \t\r\n'\"`<>|;(){}[]")
|
|
4158
|
+
token_prefix = prefix[last_separator + 1 :]
|
|
4159
|
+
if "://" in token_prefix:
|
|
4160
|
+
return True
|
|
4161
|
+
scheme = ""
|
|
4162
|
+
if token_prefix.endswith(":/"):
|
|
4163
|
+
scheme = token_prefix[:-2]
|
|
4164
|
+
elif token_prefix.endswith(":"):
|
|
4165
|
+
scheme = token_prefix[:-1]
|
|
4166
|
+
return _codex_token_prefix_is_url_scheme(scheme)
|
|
4167
|
+
|
|
4168
|
+
|
|
4169
|
+
def _codex_token_prefix_is_url_scheme(scheme: str) -> bool:
|
|
4170
|
+
return bool(scheme) and scheme[0].isalpha() and all(char.isalnum() or char in "+.-" for char in scheme)
|
|
4171
|
+
|
|
4172
|
+
|
|
4173
|
+
def _codex_command_may_read_local_content(command_text: str, *, cwd: Path | None) -> bool:
|
|
4174
|
+
if _codex_command_references_sensitive_local_source(command_text, cwd=cwd):
|
|
4175
|
+
return True
|
|
4176
|
+
if any(marker in command_text for marker in ("$(", "${", "`")):
|
|
4177
|
+
return True
|
|
4178
|
+
pipeline_segments = _split_codex_safe_read_only_pipeline(command_text)
|
|
4179
|
+
if pipeline_segments is not None:
|
|
4180
|
+
return any(
|
|
4181
|
+
_codex_pipeline_segment_may_read_local_content(segment, index=index, cwd=cwd)
|
|
4182
|
+
for index, segment in enumerate(pipeline_segments)
|
|
4183
|
+
)
|
|
4184
|
+
try:
|
|
4185
|
+
parts = _codex_shell_split(command_text)
|
|
4186
|
+
except ValueError:
|
|
4187
|
+
return True
|
|
4188
|
+
return _codex_command_parts_may_read_local_content(parts, cwd=cwd)
|
|
4189
|
+
|
|
4190
|
+
|
|
4191
|
+
def _codex_pipeline_segment_may_read_local_content(segment: str, *, index: int, cwd: Path | None) -> bool:
|
|
4192
|
+
try:
|
|
4193
|
+
parts = _codex_shell_split(segment)
|
|
4194
|
+
except ValueError:
|
|
4195
|
+
return True
|
|
4196
|
+
if not parts:
|
|
4197
|
+
return False
|
|
4198
|
+
if index == 0:
|
|
4199
|
+
return _codex_command_parts_are_environment_dump(parts) or _codex_command_parts_may_read_local_content(
|
|
4200
|
+
parts,
|
|
4201
|
+
cwd=cwd,
|
|
4202
|
+
)
|
|
4203
|
+
return _codex_command_is_read_only_source_search(segment, cwd=cwd) or _codex_command_is_read_only_source_view(
|
|
4204
|
+
segment, cwd=cwd
|
|
4205
|
+
)
|
|
4206
|
+
|
|
4207
|
+
|
|
4208
|
+
def _codex_command_parts_may_read_local_content(parts: list[str], *, cwd: Path | None) -> bool:
|
|
4209
|
+
for start in _codex_command_start_indexes(parts):
|
|
4210
|
+
previous_token = parts[start - 1] if start > 0 else None
|
|
4211
|
+
segment_parts = _codex_command_segment_parts(parts, start)
|
|
4212
|
+
if previous_token == "|":
|
|
4213
|
+
if _codex_command_sequence_is_read_only_source_inspection(segment_parts, cwd=cwd):
|
|
4214
|
+
return True
|
|
4215
|
+
continue
|
|
4216
|
+
if _codex_command_sequence_starts_with_local_reader(segment_parts, cwd=cwd):
|
|
4217
|
+
return True
|
|
4218
|
+
return False
|
|
4219
|
+
|
|
4220
|
+
|
|
4221
|
+
def _codex_command_start_indexes(parts: list[str]) -> list[int]:
|
|
4222
|
+
starts = [0] if parts else []
|
|
4223
|
+
for index, part in enumerate(parts[:-1]):
|
|
4224
|
+
if part in {"&&", "||", ";", "&", "|", "|&"}:
|
|
4225
|
+
starts.append(index + 1)
|
|
4226
|
+
return starts
|
|
4227
|
+
|
|
4228
|
+
|
|
4229
|
+
def _codex_command_segment_parts(parts: list[str], start: int) -> list[str]:
|
|
4230
|
+
end = start
|
|
4231
|
+
while end < len(parts) and parts[end] not in {"&&", "||", ";", "&", "|", "|&"}:
|
|
4232
|
+
end += 1
|
|
4233
|
+
return parts[start:end]
|
|
4234
|
+
|
|
4235
|
+
|
|
4236
|
+
def _codex_command_sequence_is_read_only_source_inspection(parts: list[str], *, cwd: Path | None) -> bool:
|
|
4237
|
+
command_parts = _codex_unwrapped_command_parts(parts)
|
|
4238
|
+
if not command_parts:
|
|
4239
|
+
return False
|
|
4240
|
+
segment = shlex.join(command_parts)
|
|
4241
|
+
return _codex_command_is_read_only_source_search(segment, cwd=cwd) or _codex_command_is_read_only_source_view(
|
|
4242
|
+
segment, cwd=cwd
|
|
4035
4243
|
)
|
|
4036
4244
|
|
|
4037
4245
|
|
|
4246
|
+
def _codex_command_sequence_starts_with_local_reader(parts: list[str], *, cwd: Path | None) -> bool:
|
|
4247
|
+
command_parts = _codex_unwrapped_command_parts(parts)
|
|
4248
|
+
if not command_parts:
|
|
4249
|
+
return False
|
|
4250
|
+
if _codex_command_parts_are_git_grep(command_parts):
|
|
4251
|
+
return True
|
|
4252
|
+
return _codex_command_part_is_local_reader(command_parts, 0, cwd=cwd)
|
|
4253
|
+
|
|
4254
|
+
|
|
4255
|
+
def _codex_command_parts_are_git_grep(parts: list[str]) -> bool:
|
|
4256
|
+
return bool(parts) and Path(parts[0]).name.lower() == "git" and _git_grep_search_args(parts[1:]) is not None
|
|
4257
|
+
|
|
4258
|
+
|
|
4259
|
+
def _codex_command_reads_environment_pipeline(command_text: str) -> bool:
|
|
4260
|
+
pipeline_segments = _split_codex_safe_read_only_pipeline(command_text)
|
|
4261
|
+
if pipeline_segments is None:
|
|
4262
|
+
return False
|
|
4263
|
+
try:
|
|
4264
|
+
first_parts = _codex_shell_split(pipeline_segments[0])
|
|
4265
|
+
except ValueError:
|
|
4266
|
+
return False
|
|
4267
|
+
return _codex_command_parts_are_environment_dump(first_parts)
|
|
4268
|
+
|
|
4269
|
+
|
|
4270
|
+
def _codex_command_parts_are_environment_dump(parts: list[str]) -> bool:
|
|
4271
|
+
if not parts:
|
|
4272
|
+
return False
|
|
4273
|
+
executable = Path(parts[0]).name.lower()
|
|
4274
|
+
if executable == "printenv":
|
|
4275
|
+
return True
|
|
4276
|
+
if executable != "env":
|
|
4277
|
+
return False
|
|
4278
|
+
if _codex_env_args_clear_environment(parts[1:]):
|
|
4279
|
+
return False
|
|
4280
|
+
return not _codex_strip_env_wrapper(parts[1:])
|
|
4281
|
+
|
|
4282
|
+
|
|
4283
|
+
def _codex_local_secret_source_label(
|
|
4284
|
+
matches: list[SecretPathMatch],
|
|
4285
|
+
*,
|
|
4286
|
+
command_text: str,
|
|
4287
|
+
) -> str | None:
|
|
4288
|
+
families: list[str] = []
|
|
4289
|
+
for match in matches:
|
|
4290
|
+
if match.family not in families:
|
|
4291
|
+
families.append(match.family)
|
|
4292
|
+
if families:
|
|
4293
|
+
if len(families) == 1:
|
|
4294
|
+
return families[0]
|
|
4295
|
+
return f"{families[0]} and other local secret files"
|
|
4296
|
+
if _codex_command_reads_environment_pipeline(command_text):
|
|
4297
|
+
return "environment variables"
|
|
4298
|
+
return None
|
|
4299
|
+
|
|
4300
|
+
|
|
4301
|
+
def _codex_tool_output_request_summary(
|
|
4302
|
+
*,
|
|
4303
|
+
tool_name: str,
|
|
4304
|
+
command_text: str,
|
|
4305
|
+
local_secret_source: str | None,
|
|
4306
|
+
) -> str:
|
|
4307
|
+
if local_secret_source is not None:
|
|
4308
|
+
return f"Codex tool `{tool_name}` read local secrets from {local_secret_source} while running `{command_text}`."
|
|
4309
|
+
return f"Codex tool `{tool_name}` produced credential-looking output while running `{command_text}`."
|
|
4310
|
+
|
|
4311
|
+
|
|
4312
|
+
def _codex_tool_output_runtime_summary(local_secret_source: str | None) -> str:
|
|
4313
|
+
if local_secret_source is not None:
|
|
4314
|
+
return f"Local secrets from {local_secret_source} reached Codex tool output."
|
|
4315
|
+
return "Requests a sensitive native tool action: credential-looking output reached Codex."
|
|
4316
|
+
|
|
4317
|
+
|
|
4318
|
+
def _codex_unwrapped_command_parts(parts: list[str]) -> list[str]:
|
|
4319
|
+
remaining = parts
|
|
4320
|
+
while remaining:
|
|
4321
|
+
executable = Path(remaining[0]).name.lower()
|
|
4322
|
+
if executable == "command":
|
|
4323
|
+
remaining = _codex_strip_command_wrapper(remaining[1:])
|
|
4324
|
+
continue
|
|
4325
|
+
if executable == "env":
|
|
4326
|
+
remaining = _codex_strip_env_wrapper(remaining[1:])
|
|
4327
|
+
continue
|
|
4328
|
+
return remaining
|
|
4329
|
+
return []
|
|
4330
|
+
|
|
4331
|
+
|
|
4332
|
+
def _codex_strip_command_wrapper(parts: list[str]) -> list[str]:
|
|
4333
|
+
index = 0
|
|
4334
|
+
while index < len(parts) and parts[index] in {"-p", "-v", "-V"}:
|
|
4335
|
+
index += 1
|
|
4336
|
+
if index < len(parts) and parts[index] == "--":
|
|
4337
|
+
index += 1
|
|
4338
|
+
return parts[index:]
|
|
4339
|
+
|
|
4340
|
+
|
|
4341
|
+
def _codex_strip_env_wrapper(parts: list[str]) -> list[str]:
|
|
4342
|
+
index = 0
|
|
4343
|
+
while index < len(parts):
|
|
4344
|
+
part = parts[index]
|
|
4345
|
+
if part == "--":
|
|
4346
|
+
return parts[index + 1 :]
|
|
4347
|
+
if part in {"-i", "-0", "--ignore-environment", "--null"}:
|
|
4348
|
+
index += 1
|
|
4349
|
+
continue
|
|
4350
|
+
if part in {"-u", "--unset", "-C", "--chdir", "-S", "--split-string"}:
|
|
4351
|
+
index += 2
|
|
4352
|
+
continue
|
|
4353
|
+
if part.startswith(("--unset=", "--chdir=", "--split-string=")):
|
|
4354
|
+
index += 1
|
|
4355
|
+
continue
|
|
4356
|
+
if part.startswith("-"):
|
|
4357
|
+
index += 1
|
|
4358
|
+
continue
|
|
4359
|
+
if "=" in part and not part.startswith("="):
|
|
4360
|
+
index += 1
|
|
4361
|
+
continue
|
|
4362
|
+
return parts[index:]
|
|
4363
|
+
return []
|
|
4364
|
+
|
|
4365
|
+
|
|
4366
|
+
def _codex_env_args_clear_environment(parts: list[str]) -> bool:
|
|
4367
|
+
for part in parts:
|
|
4368
|
+
if part == "--":
|
|
4369
|
+
return False
|
|
4370
|
+
if part in {"-i", "--ignore-environment"}:
|
|
4371
|
+
return True
|
|
4372
|
+
if part.startswith("-") or ("=" in part and not part.startswith("=")):
|
|
4373
|
+
continue
|
|
4374
|
+
return False
|
|
4375
|
+
return False
|
|
4376
|
+
|
|
4377
|
+
|
|
4378
|
+
def _codex_shell_split(command_text: str) -> list[str]:
|
|
4379
|
+
lexer = shlex.shlex(command_text, posix=True, punctuation_chars=True)
|
|
4380
|
+
lexer.whitespace_split = True
|
|
4381
|
+
lexer.commenters = ""
|
|
4382
|
+
return list(lexer)
|
|
4383
|
+
|
|
4384
|
+
|
|
4385
|
+
def _codex_command_part_is_local_reader(parts: list[str], index: int, *, cwd: Path | None) -> bool:
|
|
4386
|
+
local_read_commands = {"cat", "grep", "head", "rg", "sed", "tail"}
|
|
4387
|
+
executable = Path(parts[index]).name.lower()
|
|
4388
|
+
if executable not in local_read_commands:
|
|
4389
|
+
return False
|
|
4390
|
+
if index == 0:
|
|
4391
|
+
return True
|
|
4392
|
+
if parts[index - 1] == "|":
|
|
4393
|
+
segment = shlex.join(parts[index:])
|
|
4394
|
+
return _codex_command_is_read_only_source_search(segment, cwd=cwd) or _codex_command_is_read_only_source_view(
|
|
4395
|
+
segment, cwd=cwd
|
|
4396
|
+
)
|
|
4397
|
+
return parts[index - 1] in {"&&", "||", ";", "&", "|&"}
|
|
4398
|
+
|
|
4399
|
+
|
|
4038
4400
|
def _codex_post_tool_command_is_read_only_source_inspection(
|
|
4039
4401
|
*,
|
|
4040
4402
|
payload: dict[str, object],
|
{plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/cli/render.py
RENAMED
|
@@ -50,6 +50,7 @@ _SENSITIVE_STRING_PATTERNS: tuple[tuple[re.Pattern[str], str], ...] = (
|
|
|
50
50
|
),
|
|
51
51
|
(re.compile(r"(?i)(authorization:\s*)(bearer\s+)?[^\s,;]+"), r"\1*****"),
|
|
52
52
|
(re.compile(r"(?i)(api[-_ ]?key:\s*)[^\s,;]+"), r"\1*****"),
|
|
53
|
+
(re.compile(r"\bsk-(?:proj-)?[A-Za-z0-9_-]{20,}\b"), "*****"),
|
|
53
54
|
(re.compile(r"(?i)(bearer\s+)[^\s,;]+"), r"\1*****"),
|
|
54
55
|
(re.compile(r"(?im)\b(?:_authToken|npm[_ -]?token)\s*[:=]\s*[^\s]+"), "npm token redacted"),
|
|
55
56
|
(re.compile(r"\b(?:postgres(?:ql)?|mysql|mongodb(?:\+srv)?|redis|amqp)://[^\s]+", re.IGNORECASE), "*****"),
|
|
@@ -963,6 +964,8 @@ def _render_hook(console: Console, payload: dict[str, object]) -> None:
|
|
|
963
964
|
body.add_row("Recorded", _bool_label(bool(payload.get("recorded"))))
|
|
964
965
|
body.add_row("Artifact", str(payload.get("artifact_name") or payload.get("artifact_id") or "unknown"))
|
|
965
966
|
body.add_row("Decision", _action_text(str(payload.get("policy_action", "warn"))))
|
|
967
|
+
if payload.get("risk_summary"):
|
|
968
|
+
body.add_row("Why", str(payload.get("risk_summary")))
|
|
966
969
|
if payload.get("path_summary"):
|
|
967
970
|
body.add_row("Path", str(payload.get("path_summary")))
|
|
968
971
|
if payload.get("approval_center_url"):
|
{plugin_scanner-2.0.109 → plugin_scanner-2.0.111}/src/codex_plugin_scanner/guard/consumer/service.py
RENAMED
|
@@ -16,6 +16,7 @@ from ..config import GuardConfig
|
|
|
16
16
|
from ..incident import build_incident_context
|
|
17
17
|
from ..models import GuardArtifact, HarnessDetection, PolicyDecision
|
|
18
18
|
from ..policy import build_decision_v2, decide_action
|
|
19
|
+
from ..policy.engine import VALID_GUARD_ACTIONS
|
|
19
20
|
from ..receipts import build_receipt
|
|
20
21
|
from ..risk import artifact_risk_signals_typed, artifact_risk_summary, summarize_signals
|
|
21
22
|
from ..runtime.signals import RiskSignalV2
|
|
@@ -102,6 +103,13 @@ def _is_blocking_action(policy_action: str) -> bool:
|
|
|
102
103
|
return policy_action in {"block", "sandbox-required", "require-reapproval"}
|
|
103
104
|
|
|
104
105
|
|
|
106
|
+
def _guard_default_action(artifact: GuardArtifact) -> str | None:
|
|
107
|
+
value = artifact.metadata.get("guard_default_action")
|
|
108
|
+
if value in VALID_GUARD_ACTIONS:
|
|
109
|
+
return str(value)
|
|
110
|
+
return None
|
|
111
|
+
|
|
112
|
+
|
|
105
113
|
def _build_removed_provenance(previous: dict[str, object]) -> str:
|
|
106
114
|
scope = previous.get("source_scope")
|
|
107
115
|
config_path = previous.get("config_path")
|
|
@@ -395,7 +403,7 @@ def evaluate_detection(
|
|
|
395
403
|
"file_read_request",
|
|
396
404
|
"tool_action_request",
|
|
397
405
|
}:
|
|
398
|
-
policy_action = "require-reapproval"
|
|
406
|
+
policy_action = _guard_default_action(artifact) or "require-reapproval"
|
|
399
407
|
elif is_first_seen and configured_action is None and effective_default_action is not None:
|
|
400
408
|
policy_action = effective_default_action
|
|
401
409
|
else:
|
|
@@ -92,6 +92,8 @@ _SENSITIVE_PATH_REASONS = {
|
|
|
92
92
|
}
|
|
93
93
|
_SECRET_ASSIGNMENT_VALUE_PATTERN = r"(?:\"[^\"\r\n]+\"|'[^'\r\n]+'|[^ \t\r\n\"',}]+)"
|
|
94
94
|
_HEDERA_PRIVATE_KEY_VALUE_PATTERN = r"(?:\"(?:0x)?[0-9a-f]{64,96}\"|'(?:0x)?[0-9a-f]{64,96}'|(?:0x)?[0-9a-f]{64,96}\b)"
|
|
95
|
+
_SAMPLE_SECRET_VALUE_PATTERN = re.compile(r"(?i)\b(?:example|fake|dummy|invalid|test|canary)\b")
|
|
96
|
+
_SAMPLE_SUPPRESSIBLE_CONTENT_CLASSIFIERS = frozenset({"credential-assignment", "generic-bearer-token"})
|
|
95
97
|
_SECRET_CONTENT_PATTERNS: tuple[tuple[str, str, SecretContentSensitivity, re.Pattern[str], str], ...] = (
|
|
96
98
|
(
|
|
97
99
|
"npm-auth-token",
|
|
@@ -148,6 +150,13 @@ _SECRET_CONTENT_PATTERNS: tuple[tuple[str, str, SecretContentSensitivity, re.Pat
|
|
|
148
150
|
re.compile(r"-----BEGIN [A-Z0-9 ]*PRIVATE KEY-----", re.MULTILINE),
|
|
149
151
|
"Guard found a PEM private key header.",
|
|
150
152
|
),
|
|
153
|
+
(
|
|
154
|
+
"generic-bearer-token",
|
|
155
|
+
"generic bearer token",
|
|
156
|
+
"medium",
|
|
157
|
+
re.compile(r"(?im)\bbearer\s+[A-Za-z0-9._~+/=-]{16,}\b"),
|
|
158
|
+
"Guard found a bearer token pattern.",
|
|
159
|
+
),
|
|
151
160
|
(
|
|
152
161
|
"credential-marker",
|
|
153
162
|
"credential assignment",
|
|
@@ -272,13 +281,18 @@ def classify_legacy_secret_path_families(text: str) -> set[str]:
|
|
|
272
281
|
return {family for marker, family in LEGACY_SECRET_PATH_TEXT_MARKERS if marker in lowered}
|
|
273
282
|
|
|
274
283
|
|
|
275
|
-
def classify_secret_content(text: str | None) -> tuple[SecretContentMatch, ...]:
|
|
284
|
+
def classify_secret_content(text: str | None, *, suppress_samples: bool = True) -> tuple[SecretContentMatch, ...]:
|
|
276
285
|
if not isinstance(text, str) or not text.strip():
|
|
277
286
|
return ()
|
|
278
287
|
matches: list[SecretContentMatch] = []
|
|
279
288
|
seen: set[str] = set()
|
|
280
289
|
for classifier, family, sensitivity, pattern, reason in _SECRET_CONTENT_PATTERNS:
|
|
281
|
-
if
|
|
290
|
+
if classifier in seen:
|
|
291
|
+
continue
|
|
292
|
+
if not any(
|
|
293
|
+
not _secret_content_match_is_sample(classifier=classifier, text=match.group(0), enabled=suppress_samples)
|
|
294
|
+
for match in pattern.finditer(text)
|
|
295
|
+
):
|
|
282
296
|
continue
|
|
283
297
|
seen.add(classifier)
|
|
284
298
|
matches.append(
|
|
@@ -292,6 +306,15 @@ def classify_secret_content(text: str | None) -> tuple[SecretContentMatch, ...]:
|
|
|
292
306
|
return tuple(matches)
|
|
293
307
|
|
|
294
308
|
|
|
309
|
+
def _secret_content_match_is_sample(*, classifier: str, text: str, enabled: bool) -> bool:
|
|
310
|
+
if not enabled or classifier not in _SAMPLE_SUPPRESSIBLE_CONTENT_CLASSIFIERS:
|
|
311
|
+
return False
|
|
312
|
+
if classifier == "generic-bearer-token":
|
|
313
|
+
token = text.rsplit(None, 1)[-1]
|
|
314
|
+
return _SAMPLE_SECRET_VALUE_PATTERN.search(token) is not None and re.search(r"[A-Za-z0-9]{20,}", token) is None
|
|
315
|
+
return _SAMPLE_SECRET_VALUE_PATTERN.search(text) is not None
|
|
316
|
+
|
|
317
|
+
|
|
295
318
|
def redacted_secret_path_context(path: str) -> str | None:
|
|
296
319
|
segments = tuple(segment for segment in path.replace("\\", "/").split("/") if segment)
|
|
297
320
|
lowered_segments = tuple(segment.lower() for segment in segments)
|
|
@@ -79,11 +79,7 @@ def test_guard_json_render_redacts_after_command_specific_payload_shape(capsys)
|
|
|
79
79
|
|
|
80
80
|
|
|
81
81
|
def test_guard_json_render_redacts_private_key_without_breaking_json(capsys) -> None:
|
|
82
|
-
private_key =
|
|
83
|
-
"-----BEGIN PRIVATE KEY-----\n"
|
|
84
|
-
"super-secret-material\n"
|
|
85
|
-
"-----END PRIVATE KEY-----"
|
|
86
|
-
)
|
|
82
|
+
private_key = "-----BEGIN PRIVATE KEY-----\nsuper-secret-material\n-----END PRIVATE KEY-----"
|
|
87
83
|
|
|
88
84
|
emit_guard_payload("status", {"details": private_key}, True)
|
|
89
85
|
|
|
@@ -159,6 +155,28 @@ def test_guard_settings_json_omits_billing_flag(capsys) -> None:
|
|
|
159
155
|
assert "billing" not in output["settings"]
|
|
160
156
|
|
|
161
157
|
|
|
158
|
+
def test_guard_hook_render_shows_local_secrets_without_internal_jargon(capsys) -> None:
|
|
159
|
+
raw_secret = "sk-" + "A" * 32
|
|
160
|
+
|
|
161
|
+
emit_guard_payload(
|
|
162
|
+
"hook",
|
|
163
|
+
{
|
|
164
|
+
"recorded": True,
|
|
165
|
+
"artifact_name": "Bash local secret output",
|
|
166
|
+
"policy_action": "require-reapproval",
|
|
167
|
+
"risk_summary": f"Local secrets from local .env file. {raw_secret}",
|
|
168
|
+
"path_summary": ".env",
|
|
169
|
+
},
|
|
170
|
+
False,
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
output = _normalize_render_output(capsys.readouterr().out)
|
|
174
|
+
assert "local secrets" in output.lower()
|
|
175
|
+
assert ".env file" in output
|
|
176
|
+
assert raw_secret not in output
|
|
177
|
+
assert "credential-looking output" not in output.lower()
|
|
178
|
+
|
|
179
|
+
|
|
162
180
|
def test_guard_connect_render_defaults_sync_not_available_to_upgrade_guidance(capsys) -> None:
|
|
163
181
|
emit_guard_payload(
|
|
164
182
|
"connect",
|