plugin-scanner 2.0.63__tar.gz → 2.0.65__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.63 → plugin_scanner-2.0.65}/PKG-INFO +1 -1
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/pyproject.toml +1 -1
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/pyproject.toml.bak +1 -1
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/guard/cli/commands.py +6 -11
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/guard/cli/connect_flow.py +10 -10
- plugin_scanner-2.0.65/src/codex_plugin_scanner/guard/edge_events.py +88 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/guard/runtime/__init__.py +2 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/guard/runtime/runner.py +98 -0
- plugin_scanner-2.0.65/src/codex_plugin_scanner/guard/schemas/guard_event_v1.py +74 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/guard/store.py +95 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/version.py +1 -1
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/test_guard_claude_adapter.py +5 -1
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/test_guard_cli.py +33 -21
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/test_guard_connect_flow.py +9 -9
- plugin_scanner-2.0.65/tests/test_guard_event_schema_v1.py +260 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/test_guard_runtime.py +10 -2
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/test_guard_surface_server.py +5 -1
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/.clusterfuzzlite/Dockerfile +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/.clusterfuzzlite/build.sh +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/.clusterfuzzlite/project.yaml +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/.clusterfuzzlite/requirements-atheris.txt +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/.dockerignore +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/.github/CODEOWNERS +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/.github/ISSUE_TEMPLATE/bug-report.yml +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/.github/ISSUE_TEMPLATE/feature-request.yml +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/.github/dependabot.yml +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/.github/workflows/ci.yml +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/.github/workflows/codeql.yml +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/.github/workflows/dependabot-uv-lock.yml +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/.github/workflows/fuzz.yml +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/.github/workflows/harness-smoke.yml +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/.github/workflows/publish.yml +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/.github/workflows/scorecard.yml +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/.gitignore +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/.pre-commit-hooks.yaml +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/CONTRIBUTING.md +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/Dockerfile +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/LICENSE +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/README.md +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/SECURITY.md +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/dashboard/index.html +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/dashboard/package.json +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/dashboard/pnpm-lock.yaml +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/dashboard/public/brand/Logo_Whole.png +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/dashboard/src/app.tsx +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/dashboard/src/approval-center-layout.tsx +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/dashboard/src/approval-center-primitives.tsx +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/dashboard/src/approval-center-utils.ts +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/dashboard/src/fleet-workspace.tsx +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/dashboard/src/guard-api.ts +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/dashboard/src/guard-demo.ts +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/dashboard/src/guard-types.ts +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/dashboard/src/main.tsx +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/dashboard/src/receipts-workspace.tsx +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/dashboard/src/runtime-overview.tsx +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/dashboard/src/styles.css +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/dashboard/src/vite-env.d.ts +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/dashboard/tsconfig.json +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/dashboard/vite.config.ts +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/docker-requirements.txt +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/docs/guard/approval-audit.md +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/docs/guard/architecture.md +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/docs/guard/get-started.md +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/docs/guard/harness-support.md +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/docs/guard/local-vs-cloud.md +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/docs/guard/testing-matrix.md +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/docs/trust/mcp-trust-draft.md +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/docs/trust/plugin-trust-draft.md +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/docs/trust/skill-trust-local.md +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/fuzzers/manifest_fuzzer.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/requirements.txt +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/schemas/plugin-quality.v1.json +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/schemas/scan-result.v1.json +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/schemas/verify-result.v1.json +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/__init__.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/action_runner.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/argparse_utils.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/checks/__init__.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/checks/best_practices.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/checks/claude.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/checks/code_quality.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/checks/ecosystem_common.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/checks/gemini.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/checks/manifest.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/checks/manifest_support.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/checks/marketplace.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/checks/mcp_security.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/checks/opencode.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/checks/operational_security.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/checks/security.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/checks/skill_security.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/cli.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/cli_ui.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/config.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/ecosystems/__init__.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/ecosystems/base.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/ecosystems/claude.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/ecosystems/codex.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/ecosystems/detect.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/ecosystems/gemini.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/ecosystems/opencode.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/ecosystems/registry.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/ecosystems/types.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/github_reporting.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/guard/__init__.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/guard/adapters/__init__.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/guard/adapters/antigravity.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/guard/adapters/base.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/guard/adapters/claude_code.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/guard/adapters/codex.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/guard/adapters/copilot.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/guard/adapters/cursor.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/guard/adapters/gemini.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/guard/adapters/hermes.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/guard/adapters/mcp_servers.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/guard/adapters/opencode.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/guard/adapters/opencode_artifacts.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/guard/approvals.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/guard/bridge/__init__.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/guard/capabilities.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/guard/cli/__init__.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/guard/cli/approval_commands.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/guard/cli/bootstrap.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/guard/cli/install_commands.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/guard/cli/product.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/guard/cli/prompt.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/guard/cli/render.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/guard/cli/update_commands.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/guard/codex_config.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/guard/config.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/guard/consumer/__init__.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/guard/consumer/service.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/guard/daemon/__init__.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/guard/daemon/client.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/guard/daemon/manager.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/guard/daemon/server.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/guard/daemon/static/assets/guard-dashboard.js +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/guard/daemon/static/assets/index.css +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/guard/daemon/static/brand/Logo_Whole.png +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/guard/daemon/static/index.html +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/guard/incident.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/guard/launcher.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/guard/mcp_tool_calls.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/guard/models.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/guard/policy/__init__.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/guard/policy/engine.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/guard/protect.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/guard/proxy/__init__.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/guard/proxy/remote.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/guard/proxy/runtime_mcp.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/guard/proxy/stdio.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/guard/receipts/__init__.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/guard/receipts/manager.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/guard/risk.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/guard/runtime/secret_file_requests.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/guard/runtime/surface_server.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/guard/schemas/__init__.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/guard/schemas/consumer_mode.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/guard/schemas/surface_server.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/guard/shims.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/guard/store_approvals.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/guard/store_connect.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/guard/types.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/integrations/__init__.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/integrations/cisco_mcp_scanner.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/integrations/cisco_skill_scanner.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/lint_fixes.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/marketplace_support.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/models.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/path_support.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/policy.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/quality_artifact.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/repo_detect.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/reporting.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/rules/__init__.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/rules/registry.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/rules/specs.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/scanner.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/submission.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/suppressions.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/trust_domain_scoring.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/trust_helpers.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/trust_mcp_scoring.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/trust_models.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/trust_plugin_scoring.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/trust_scoring.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/trust_skill_scoring.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/trust_specs.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/verification.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/__init__.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/conftest.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/fixtures/__init__.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/fixtures/bad-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/fixtures/bad-plugin/.mcp.json +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/fixtures/bad-plugin/secrets.js +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/fixtures/claude-plugin-good/.claude-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/fixtures/claude-plugin-good/LICENSE +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/fixtures/claude-plugin-good/README.md +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/fixtures/claude-plugin-good/SECURITY.md +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/fixtures/claude-plugin-good/hooks/hooks.json +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/fixtures/claude-plugin-good/skills/example/SKILL.md +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/fixtures/code-quality-bad/evil.js +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/fixtures/code-quality-bad/inject.js +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/fixtures/gemini-extension-good/GEMINI.md +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/fixtures/gemini-extension-good/LICENSE +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/fixtures/gemini-extension-good/README.md +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/fixtures/gemini-extension-good/SECURITY.md +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/fixtures/gemini-extension-good/commands/hello.toml +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/fixtures/gemini-extension-good/gemini-extension.json +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/fixtures/good-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/fixtures/good-plugin/.codexignore +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/fixtures/good-plugin/LICENSE +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/fixtures/good-plugin/README.md +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/fixtures/good-plugin/SECURITY.md +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/fixtures/good-plugin/assets/icon.svg +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/fixtures/good-plugin/assets/logo.svg +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/fixtures/good-plugin/assets/screenshot.svg +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/fixtures/good-plugin/skills/example/SKILL.md +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/fixtures/guard-codex-malicious-mcp/.codex/config.toml +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/fixtures/hermes-plugin-evil/config.yaml +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/fixtures/hermes-plugin-evil/mcp_servers.json +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/fixtures/hermes-plugin-evil/skills/security/malicious/SKILL.md +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/SKILL.md +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/references/api-setup.md +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/scripts/deploy.sh +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/fixtures/hermes-plugin-evil/skills/utils/benign/SKILL.md +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/fixtures/malformed-json/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/fixtures/malicious-skill-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/fixtures/malicious-skill-plugin/.codexignore +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/fixtures/malicious-skill-plugin/LICENSE +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/fixtures/malicious-skill-plugin/README.md +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/fixtures/malicious-skill-plugin/SECURITY.md +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/fixtures/malicious-skill-plugin/skills/leaky-skill/SKILL.md +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/fixtures/mcp-canary-server.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/fixtures/minimal-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/fixtures/missing-fields/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/fixtures/mit-license/LICENSE +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/fixtures/multi-ecosystem-repo/codex-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/fixtures/multi-ecosystem-repo/codex-plugin/LICENSE +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/fixtures/multi-ecosystem-repo/codex-plugin/README.md +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/fixtures/multi-ecosystem-repo/codex-plugin/SECURITY.md +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/fixtures/multi-ecosystem-repo/gemini-ext/README.md +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/fixtures/multi-ecosystem-repo/gemini-ext/gemini-extension.json +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/fixtures/multi-plugin-repo/.agents/plugins/marketplace.json +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/.codexignore +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/LICENSE +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/README.md +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/SECURITY.md +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/skills/example/SKILL.md +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/fixtures/multi-plugin-repo/plugins/beta-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/fixtures/multi-plugin-repo/plugins/beta-plugin/skills/example/SKILL.md +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/fixtures/no-version/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/fixtures/opencode-good/.opencode/commands/hello.md +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/fixtures/opencode-good/.opencode/plugins/example.ts +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/fixtures/opencode-good/LICENSE +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/fixtures/opencode-good/README.md +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/fixtures/opencode-good/SECURITY.md +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/fixtures/opencode-good/opencode.jsonc +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/fixtures/skills-missing-dir/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/fixtures/skills-no-frontmatter/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/fixtures/skills-no-frontmatter/skills/bad-skill/SKILL.md +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/fixtures/with-marketplace/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/fixtures/with-marketplace/marketplace-broken.json +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/fixtures/with-marketplace/marketplace.json +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/test-trust-scoring.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/test-trust-specs.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/test_action_runner.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/test_best_practices.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/test_cisco_install_surfaces.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/test_cli.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/test_code_quality.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/test_config.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/test_coverage_remaining.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/test_ecosystems.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/test_edge_cases.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/test_final_coverage.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/test_guard_approvals.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/test_guard_bootstrap.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/test_guard_capabilities.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/test_guard_codex_e2e.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/test_guard_codex_install.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/test_guard_codex_proxy.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/test_guard_config_paths.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/test_guard_consumer_mode.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/test_guard_copilot_adapter.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/test_guard_copilot_proxy.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/test_guard_daemon_manager.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/test_guard_events.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/test_guard_launch_env.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/test_guard_opencode_proxy.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/test_guard_product_flow.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/test_guard_protect.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/test_guard_render.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/test_guard_risk.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/test_guard_store_migrations.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/test_guard_verdicts.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/test_hermes_adapter.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/test_integration.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/test_lint_fixes.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/test_live_cisco_smoke.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/test_manifest.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/test_marketplace.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/test_mcp_security.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/test_operational_security.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/test_policy.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/test_quality_artifact.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/test_rule_registry.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/test_scanner.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/test_schema_contracts.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/test_security.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/test_security_ops.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/test_skill_security.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/test_submission.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/test_trust_scoring.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/test_trust_specs.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/test_verification.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/tests/test_versioning.py +0 -0
- {plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/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.65
|
|
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.65"
|
|
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.65"
|
|
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.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/guard/cli/commands.py
RENAMED
|
@@ -2438,22 +2438,17 @@ def _claude_prompt_additional_context(
|
|
|
2438
2438
|
return None
|
|
2439
2439
|
if _prompt_requires_hard_block(artifact):
|
|
2440
2440
|
return None
|
|
2441
|
-
briefing_sentence = "HOL Guard
|
|
2441
|
+
briefing_sentence = "HOL Guard will intercept Claude's next sensitive action and open a branded approval question."
|
|
2442
2442
|
if "secret_read" in _prompt_request_classes(artifact):
|
|
2443
2443
|
briefing_sentence = (
|
|
2444
|
-
"HOL Guard
|
|
2445
|
-
"
|
|
2446
|
-
)
|
|
2447
|
-
else:
|
|
2448
|
-
briefing_sentence = (
|
|
2449
|
-
"HOL Guard intercepted Claude's next sensitive action and opened the approval dialog shown below."
|
|
2444
|
+
"HOL Guard will intercept Claude's next attempt to access local secrets and open a branded approval "
|
|
2445
|
+
"question to protect you."
|
|
2450
2446
|
)
|
|
2451
2447
|
return (
|
|
2452
2448
|
f"{_ensure_terminal_punctuation(native_reason)} "
|
|
2453
|
-
"
|
|
2454
|
-
"
|
|
2455
|
-
"
|
|
2456
|
-
"retry the sensitive action. Then tell the user exactly: "
|
|
2449
|
+
"Do not ask for approval at the prompt stage. Attempt the intended sensitive tool once so HOL Guard can "
|
|
2450
|
+
"evaluate the exact tool, path, and arguments, then route that concrete action into a HOL Guard approval "
|
|
2451
|
+
"question with Allow once, Allow during this session, and Keep blocked. First tell the user exactly: "
|
|
2457
2452
|
f"'{briefing_sentence}' "
|
|
2458
2453
|
"Attempt that sensitive tool at most once. If HOL Guard or Claude denies it, do not retry the same sensitive "
|
|
2459
2454
|
"action automatically. Instead, tell the user approval is required in Claude to continue."
|
{plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/guard/cli/connect_flow.py
RENAMED
|
@@ -107,36 +107,37 @@ def run_guard_connect_command(
|
|
|
107
107
|
sync_payload = sync_receipts(store)
|
|
108
108
|
except GuardSyncNotAvailableError as plan_error:
|
|
109
109
|
plan_msg = str(plan_error).strip() or "Cloud sync requires a paid Guard plan."
|
|
110
|
+
pending_sync_payload = dict(runtime_sync_summary)
|
|
111
|
+
pending_sync_payload["synced_at"] = None
|
|
110
112
|
pending_state = _record_connect_result(
|
|
111
113
|
daemon_client=daemon_client,
|
|
112
114
|
store=store,
|
|
113
115
|
request_id=str(connect_request["request_id"]),
|
|
114
|
-
status="
|
|
115
|
-
milestone="
|
|
116
|
+
status="retry_required",
|
|
117
|
+
milestone="first_sync_failed",
|
|
116
118
|
reason=plan_msg,
|
|
119
|
+
sync=pending_sync_payload,
|
|
117
120
|
)
|
|
118
121
|
return build_connect_payload(
|
|
119
122
|
state=pending_state,
|
|
120
123
|
browser_opened=browser_opened,
|
|
121
124
|
connect_url=browser_url,
|
|
122
125
|
sync_url=sync_url,
|
|
123
|
-
connected=
|
|
126
|
+
connected=False,
|
|
127
|
+
sync=pending_sync_payload,
|
|
124
128
|
sync_available=False,
|
|
125
129
|
sync_message=plan_msg,
|
|
126
130
|
)
|
|
127
131
|
except (RuntimeError, OSError, urllib.error.URLError, json.JSONDecodeError) as error:
|
|
128
132
|
sync_message = str(error)
|
|
129
|
-
if runtime_sync_error and not _is_paid_plan_sync_error(sync_message):
|
|
130
|
-
sync_message = runtime_sync_error
|
|
131
|
-
sync_is_plan_limited = _is_paid_plan_sync_error(sync_message)
|
|
132
133
|
pending_sync_payload = dict(runtime_sync_summary)
|
|
133
134
|
pending_sync_payload["synced_at"] = None
|
|
134
135
|
pending_state = _record_connect_result(
|
|
135
136
|
daemon_client=daemon_client,
|
|
136
137
|
store=store,
|
|
137
138
|
request_id=str(connect_request["request_id"]),
|
|
138
|
-
status="
|
|
139
|
-
milestone="
|
|
139
|
+
status="retry_required",
|
|
140
|
+
milestone="first_sync_failed",
|
|
140
141
|
reason=sync_message,
|
|
141
142
|
sync=pending_sync_payload,
|
|
142
143
|
)
|
|
@@ -145,10 +146,9 @@ def run_guard_connect_command(
|
|
|
145
146
|
browser_opened=browser_opened,
|
|
146
147
|
connect_url=browser_url,
|
|
147
148
|
sync_url=sync_url,
|
|
148
|
-
connected=
|
|
149
|
+
connected=False,
|
|
149
150
|
sync=pending_sync_payload,
|
|
150
151
|
sync_message=sync_message,
|
|
151
|
-
sync_available=False if sync_is_plan_limited else None,
|
|
152
152
|
)
|
|
153
153
|
sync_payload["runtime_session_synced_at"] = runtime_sync_summary["runtime_session_synced_at"]
|
|
154
154
|
sync_payload["runtime_session_id"] = runtime_sync_summary["runtime_session_id"]
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"""Builders for Guard Cloud v1 events emitted by the local edge runtime."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import hashlib
|
|
6
|
+
from typing import cast
|
|
7
|
+
|
|
8
|
+
from .models import GuardReceipt
|
|
9
|
+
from .schemas.guard_event_v1 import GuardEventType, GuardEventV1
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def build_receipt_event(
|
|
13
|
+
receipt: GuardReceipt,
|
|
14
|
+
*,
|
|
15
|
+
device_id: str | None = None,
|
|
16
|
+
workspace_id: str | None = None,
|
|
17
|
+
) -> GuardEventV1:
|
|
18
|
+
payload: dict[str, object] = {
|
|
19
|
+
"receiptId": receipt.receipt_id,
|
|
20
|
+
"harness": receipt.harness,
|
|
21
|
+
"artifactId": receipt.artifact_id,
|
|
22
|
+
"artifactHash": receipt.artifact_hash,
|
|
23
|
+
"artifactName": receipt.artifact_name,
|
|
24
|
+
"sourceScope": receipt.source_scope,
|
|
25
|
+
"policyDecision": receipt.policy_decision,
|
|
26
|
+
"capabilitiesSummary": receipt.capabilities_summary,
|
|
27
|
+
"changedCapabilities": list(receipt.changed_capabilities),
|
|
28
|
+
"provenanceSummary": receipt.provenance_summary,
|
|
29
|
+
"userOverride": receipt.user_override,
|
|
30
|
+
}
|
|
31
|
+
event_id = f"guard-event-{_fingerprint('receipt.created', receipt.receipt_id)[:32]}"
|
|
32
|
+
return GuardEventV1(
|
|
33
|
+
event_id=event_id,
|
|
34
|
+
idempotency_key=f"receipt.created:{receipt.receipt_id}",
|
|
35
|
+
event_type="receipt.created",
|
|
36
|
+
source="edge",
|
|
37
|
+
occurred_at=receipt.timestamp,
|
|
38
|
+
workspace_id=workspace_id,
|
|
39
|
+
device_id=device_id,
|
|
40
|
+
payload=payload,
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def build_approval_event(
|
|
45
|
+
*,
|
|
46
|
+
request_id: str,
|
|
47
|
+
event_type: str,
|
|
48
|
+
occurred_at: str,
|
|
49
|
+
payload: dict[str, object],
|
|
50
|
+
device_id: str | None = None,
|
|
51
|
+
workspace_id: str | None = None,
|
|
52
|
+
) -> GuardEventV1:
|
|
53
|
+
if event_type not in {"approval.created", "approval.resolved"}:
|
|
54
|
+
raise ValueError("Approval event type must be approval.created or approval.resolved")
|
|
55
|
+
return GuardEventV1(
|
|
56
|
+
event_id=f"guard-event-{_fingerprint(event_type, request_id, occurred_at)[:32]}",
|
|
57
|
+
idempotency_key=f"{event_type}:{request_id}:{occurred_at}",
|
|
58
|
+
event_type=cast(GuardEventType, event_type),
|
|
59
|
+
source="approval-center",
|
|
60
|
+
occurred_at=occurred_at,
|
|
61
|
+
workspace_id=workspace_id,
|
|
62
|
+
device_id=device_id,
|
|
63
|
+
payload=payload,
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def build_policy_event(
|
|
68
|
+
*,
|
|
69
|
+
policy_key: str,
|
|
70
|
+
occurred_at: str,
|
|
71
|
+
payload: dict[str, object],
|
|
72
|
+
device_id: str | None = None,
|
|
73
|
+
workspace_id: str | None = None,
|
|
74
|
+
) -> GuardEventV1:
|
|
75
|
+
return GuardEventV1(
|
|
76
|
+
event_id=f"guard-event-{_fingerprint('policy.changed', policy_key, occurred_at)[:32]}",
|
|
77
|
+
idempotency_key=f"policy.changed:{policy_key}:{occurred_at}",
|
|
78
|
+
event_type="policy.changed",
|
|
79
|
+
source="policy",
|
|
80
|
+
occurred_at=occurred_at,
|
|
81
|
+
workspace_id=workspace_id,
|
|
82
|
+
device_id=device_id,
|
|
83
|
+
payload=payload,
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def _fingerprint(*parts: str) -> str:
|
|
88
|
+
return hashlib.sha256(":".join(parts).encode()).hexdigest()
|
{plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/guard/runtime/__init__.py
RENAMED
|
@@ -4,6 +4,7 @@ from .runner import (
|
|
|
4
4
|
GuardSyncNotAvailableError,
|
|
5
5
|
GuardSyncNotConfiguredError,
|
|
6
6
|
guard_run,
|
|
7
|
+
sync_guard_events,
|
|
7
8
|
sync_receipts,
|
|
8
9
|
sync_runtime_session,
|
|
9
10
|
)
|
|
@@ -12,6 +13,7 @@ __all__ = [
|
|
|
12
13
|
"GuardSyncNotAvailableError",
|
|
13
14
|
"GuardSyncNotConfiguredError",
|
|
14
15
|
"guard_run",
|
|
16
|
+
"sync_guard_events",
|
|
15
17
|
"sync_receipts",
|
|
16
18
|
"sync_runtime_session",
|
|
17
19
|
]
|
{plugin_scanner-2.0.63 → plugin_scanner-2.0.65}/src/codex_plugin_scanner/guard/runtime/runner.py
RENAMED
|
@@ -480,10 +480,69 @@ def sync_receipts(store: GuardStore) -> dict[str, object]:
|
|
|
480
480
|
"inventory": 0,
|
|
481
481
|
"inventory_tracked": len(inventory),
|
|
482
482
|
}
|
|
483
|
+
summary["guard_events_v1"] = sync_guard_events(store)
|
|
483
484
|
store.set_sync_payload("sync_summary", summary, now)
|
|
484
485
|
return summary
|
|
485
486
|
|
|
486
487
|
|
|
488
|
+
def sync_guard_events(store: GuardStore) -> dict[str, object]:
|
|
489
|
+
"""Push pending GuardEventV1 envelopes to Guard Cloud."""
|
|
490
|
+
|
|
491
|
+
credentials = store.get_sync_credentials()
|
|
492
|
+
if credentials is None:
|
|
493
|
+
raise GuardSyncNotConfiguredError("Guard is not logged in.")
|
|
494
|
+
sync_url = _guard_events_sync_url(str(credentials["sync_url"]))
|
|
495
|
+
total_events = 0
|
|
496
|
+
total_accepted = 0
|
|
497
|
+
synced_at = _now()
|
|
498
|
+
while True:
|
|
499
|
+
pending_events = store.list_guard_events_v1(uploaded=False, limit=200)
|
|
500
|
+
if not pending_events:
|
|
501
|
+
break
|
|
502
|
+
body = json.dumps({"events": [event["payload"] for event in pending_events]}).encode("utf-8")
|
|
503
|
+
request = urllib.request.Request(
|
|
504
|
+
sync_url,
|
|
505
|
+
data=body,
|
|
506
|
+
method="POST",
|
|
507
|
+
headers=_guard_sync_headers(str(credentials["token"])),
|
|
508
|
+
)
|
|
509
|
+
try:
|
|
510
|
+
payload = _urlopen_json_with_timeout_retry(
|
|
511
|
+
request=request,
|
|
512
|
+
timeout_seconds=_SYNC_HTTP_TIMEOUT_SECONDS,
|
|
513
|
+
retry_timeout_seconds=_SYNC_HTTP_RETRY_TIMEOUT_SECONDS,
|
|
514
|
+
)
|
|
515
|
+
except urllib.error.HTTPError as error:
|
|
516
|
+
if error.code == 404:
|
|
517
|
+
summary = {
|
|
518
|
+
"synced_at": synced_at,
|
|
519
|
+
"events": total_events,
|
|
520
|
+
"accepted": total_accepted,
|
|
521
|
+
"sync_skipped": True,
|
|
522
|
+
"sync_reason": "guard_events_endpoint_unavailable",
|
|
523
|
+
}
|
|
524
|
+
store.set_sync_payload("guard_events_v1_summary", summary, synced_at)
|
|
525
|
+
return summary
|
|
526
|
+
if error.code == 403:
|
|
527
|
+
is_plan, message = _check_plan_restriction_403(error)
|
|
528
|
+
if is_plan:
|
|
529
|
+
raise GuardSyncNotAvailableError(message) from error
|
|
530
|
+
raise RuntimeError(message) from error
|
|
531
|
+
raise RuntimeError(_sync_http_error_message(error)) from error
|
|
532
|
+
except OSError as error:
|
|
533
|
+
raise RuntimeError(_sync_url_error_message(error)) from error
|
|
534
|
+
completed_ids = _completed_guard_event_ids(payload)
|
|
535
|
+
synced_at = _sync_timestamp(payload)
|
|
536
|
+
uploaded = store.mark_guard_events_v1_uploaded(completed_ids, synced_at)
|
|
537
|
+
total_events += len(pending_events)
|
|
538
|
+
total_accepted += uploaded
|
|
539
|
+
if uploaded == 0 or len(pending_events) < 200:
|
|
540
|
+
break
|
|
541
|
+
summary = {"synced_at": synced_at, "events": total_events, "accepted": total_accepted}
|
|
542
|
+
store.set_sync_payload("guard_events_v1_summary", summary, synced_at)
|
|
543
|
+
return summary
|
|
544
|
+
|
|
545
|
+
|
|
487
546
|
def sync_runtime_session(
|
|
488
547
|
store: GuardStore,
|
|
489
548
|
*,
|
|
@@ -951,6 +1010,45 @@ def _normalized_runtime_sessions_sync_url(sync_url: str) -> str:
|
|
|
951
1010
|
)
|
|
952
1011
|
|
|
953
1012
|
|
|
1013
|
+
def _guard_events_sync_url(sync_url: str) -> str:
|
|
1014
|
+
parsed = urllib.parse.urlsplit(_normalized_receipts_sync_url(sync_url))
|
|
1015
|
+
if parsed.path.rstrip("/").endswith("/api/v1/guard/events"):
|
|
1016
|
+
return urllib.parse.urlunsplit((parsed.scheme, parsed.netloc, parsed.path.rstrip("/"), parsed.query, ""))
|
|
1017
|
+
path = parsed.path.rstrip("/")
|
|
1018
|
+
for suffix in (
|
|
1019
|
+
"/api/guard/receipts/sync",
|
|
1020
|
+
"/guard/receipts/sync",
|
|
1021
|
+
"/registry/api/v1/guard/receipts/sync",
|
|
1022
|
+
):
|
|
1023
|
+
if path.endswith(suffix):
|
|
1024
|
+
path = path[: -len(suffix)]
|
|
1025
|
+
break
|
|
1026
|
+
return urllib.parse.urlunsplit(
|
|
1027
|
+
(
|
|
1028
|
+
parsed.scheme,
|
|
1029
|
+
parsed.netloc,
|
|
1030
|
+
path.rstrip("/") + "/api/v1/guard/events",
|
|
1031
|
+
parsed.query,
|
|
1032
|
+
"",
|
|
1033
|
+
)
|
|
1034
|
+
)
|
|
1035
|
+
|
|
1036
|
+
|
|
1037
|
+
def _completed_guard_event_ids(payload: dict[str, object]) -> list[str]:
|
|
1038
|
+
statuses = payload.get("statuses")
|
|
1039
|
+
if not isinstance(statuses, list):
|
|
1040
|
+
return []
|
|
1041
|
+
completed: list[str] = []
|
|
1042
|
+
for item in statuses:
|
|
1043
|
+
if not isinstance(item, dict):
|
|
1044
|
+
continue
|
|
1045
|
+
status = str(item.get("status") or "")
|
|
1046
|
+
event_id = item.get("eventId")
|
|
1047
|
+
if status in {"accepted", "duplicate", "rejected"} and isinstance(event_id, str):
|
|
1048
|
+
completed.append(event_id)
|
|
1049
|
+
return completed
|
|
1050
|
+
|
|
1051
|
+
|
|
954
1052
|
def _cloud_sync_receipts_payload(store: GuardStore, receipts: list[dict[str, object]]) -> list[dict[str, object]]:
|
|
955
1053
|
device_id, device_name = _guard_device_metadata(store)
|
|
956
1054
|
return [_cloud_sync_receipt_payload(receipt, device_id=device_id, device_name=device_name) for receipt in receipts]
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"""Guard Cloud event schema shared by the edge runtime and v1 ingest API."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass, field
|
|
6
|
+
from typing import Literal, cast
|
|
7
|
+
|
|
8
|
+
GuardEventSource = Literal["edge", "approval-center", "policy", "protect-api"]
|
|
9
|
+
GuardEventType = Literal["receipt.created", "approval.created", "approval.resolved", "policy.changed"]
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclass(frozen=True, slots=True)
|
|
13
|
+
class GuardEventV1:
|
|
14
|
+
"""Versioned event envelope for replay-safe Guard Cloud sync."""
|
|
15
|
+
|
|
16
|
+
event_id: str
|
|
17
|
+
idempotency_key: str
|
|
18
|
+
event_type: GuardEventType
|
|
19
|
+
source: GuardEventSource
|
|
20
|
+
occurred_at: str
|
|
21
|
+
workspace_id: str | None = None
|
|
22
|
+
device_id: str | None = None
|
|
23
|
+
payload: dict[str, object] = field(default_factory=dict)
|
|
24
|
+
schema_version: str = "guard.event.v1"
|
|
25
|
+
|
|
26
|
+
def to_dict(self) -> dict[str, object]:
|
|
27
|
+
return {
|
|
28
|
+
"schemaVersion": self.schema_version,
|
|
29
|
+
"eventId": self.event_id,
|
|
30
|
+
"idempotencyKey": self.idempotency_key,
|
|
31
|
+
"eventType": self.event_type,
|
|
32
|
+
"source": self.source,
|
|
33
|
+
"occurredAt": self.occurred_at,
|
|
34
|
+
"workspaceId": self.workspace_id,
|
|
35
|
+
"deviceId": self.device_id,
|
|
36
|
+
"payload": self.payload,
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
@classmethod
|
|
40
|
+
def from_dict(cls, payload: dict[str, object]) -> GuardEventV1:
|
|
41
|
+
schema_version = str(payload.get("schemaVersion") or "")
|
|
42
|
+
if schema_version != "guard.event.v1":
|
|
43
|
+
raise ValueError("Guard event schemaVersion must be guard.event.v1")
|
|
44
|
+
event_id = _required_string(payload, "eventId")
|
|
45
|
+
idempotency_key = _required_string(payload, "idempotencyKey")
|
|
46
|
+
event_type = _required_string(payload, "eventType")
|
|
47
|
+
source = _required_string(payload, "source")
|
|
48
|
+
occurred_at = _required_string(payload, "occurredAt")
|
|
49
|
+
event_payload = payload.get("payload")
|
|
50
|
+
if not isinstance(event_payload, dict):
|
|
51
|
+
raise ValueError("Guard event payload must be an object")
|
|
52
|
+
if event_type not in {"receipt.created", "approval.created", "approval.resolved", "policy.changed"}:
|
|
53
|
+
raise ValueError(f"Unsupported Guard event type: {event_type}")
|
|
54
|
+
if source not in {"edge", "approval-center", "policy", "protect-api"}:
|
|
55
|
+
raise ValueError(f"Unsupported Guard event source: {source}")
|
|
56
|
+
workspace_id = payload.get("workspaceId")
|
|
57
|
+
device_id = payload.get("deviceId")
|
|
58
|
+
return cls(
|
|
59
|
+
event_id=event_id,
|
|
60
|
+
idempotency_key=idempotency_key,
|
|
61
|
+
event_type=cast(GuardEventType, event_type),
|
|
62
|
+
source=cast(GuardEventSource, source),
|
|
63
|
+
occurred_at=occurred_at,
|
|
64
|
+
workspace_id=workspace_id if isinstance(workspace_id, str) else None,
|
|
65
|
+
device_id=device_id if isinstance(device_id, str) else None,
|
|
66
|
+
payload={str(key): value for key, value in event_payload.items()},
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _required_string(payload: dict[str, object], key: str) -> str:
|
|
71
|
+
value = payload.get(key)
|
|
72
|
+
if not isinstance(value, str) or not value.strip():
|
|
73
|
+
raise ValueError(f"Guard event {key} must be a non-empty string")
|
|
74
|
+
return value
|
|
@@ -17,7 +17,9 @@ from uuid import uuid4
|
|
|
17
17
|
|
|
18
18
|
from cryptography.fernet import Fernet, InvalidToken
|
|
19
19
|
|
|
20
|
+
from .edge_events import build_receipt_event
|
|
20
21
|
from .models import GuardApprovalRequest, GuardArtifact, GuardReceipt, GuardRuntimeState, PolicyDecision
|
|
22
|
+
from .schemas.guard_event_v1 import GuardEventV1
|
|
21
23
|
from .store_approvals import (
|
|
22
24
|
add_approval_request as persist_approval_request,
|
|
23
25
|
)
|
|
@@ -548,6 +550,20 @@ class GuardStore:
|
|
|
548
550
|
)
|
|
549
551
|
""",
|
|
550
552
|
"""
|
|
553
|
+
create table if not exists guard_cloud_events (
|
|
554
|
+
event_id text primary key,
|
|
555
|
+
idempotency_key text not null unique,
|
|
556
|
+
event_type text not null,
|
|
557
|
+
payload_json text not null,
|
|
558
|
+
occurred_at text not null,
|
|
559
|
+
uploaded_at text
|
|
560
|
+
)
|
|
561
|
+
""",
|
|
562
|
+
"""
|
|
563
|
+
create index if not exists idx_guard_cloud_events_sync
|
|
564
|
+
on guard_cloud_events (uploaded_at, occurred_at)
|
|
565
|
+
""",
|
|
566
|
+
"""
|
|
551
567
|
create table if not exists guard_runtime_state (
|
|
552
568
|
state_key text primary key,
|
|
553
569
|
session_id text not null,
|
|
@@ -1213,6 +1229,19 @@ class GuardStore:
|
|
|
1213
1229
|
receipt.timestamp,
|
|
1214
1230
|
),
|
|
1215
1231
|
)
|
|
1232
|
+
self._ensure_local_device(connection)
|
|
1233
|
+
row = connection.execute(
|
|
1234
|
+
"select installation_id from guard_devices where device_key = ?",
|
|
1235
|
+
(_DEVICE_ROW_KEY,),
|
|
1236
|
+
).fetchone()
|
|
1237
|
+
device_id = str(row["installation_id"]) if row is not None else None
|
|
1238
|
+
self._add_guard_event_v1(
|
|
1239
|
+
connection,
|
|
1240
|
+
build_receipt_event(
|
|
1241
|
+
receipt,
|
|
1242
|
+
device_id=device_id,
|
|
1243
|
+
),
|
|
1244
|
+
)
|
|
1216
1245
|
|
|
1217
1246
|
def list_receipts(self, limit: int = 50) -> list[dict[str, object]]:
|
|
1218
1247
|
with self._connect() as connection:
|
|
@@ -1698,6 +1727,72 @@ class GuardStore:
|
|
|
1698
1727
|
(state_key,),
|
|
1699
1728
|
)
|
|
1700
1729
|
|
|
1730
|
+
def add_guard_event_v1(self, event: GuardEventV1) -> None:
|
|
1731
|
+
with self._connect() as connection:
|
|
1732
|
+
self._add_guard_event_v1(connection, event)
|
|
1733
|
+
|
|
1734
|
+
@staticmethod
|
|
1735
|
+
def _add_guard_event_v1(connection: sqlite3.Connection, event: GuardEventV1) -> None:
|
|
1736
|
+
payload = event.to_dict()
|
|
1737
|
+
connection.execute(
|
|
1738
|
+
"""
|
|
1739
|
+
insert or ignore into guard_cloud_events (
|
|
1740
|
+
event_id, idempotency_key, event_type, payload_json, occurred_at, uploaded_at
|
|
1741
|
+
)
|
|
1742
|
+
values (?, ?, ?, ?, ?, null)
|
|
1743
|
+
""",
|
|
1744
|
+
(
|
|
1745
|
+
event.event_id,
|
|
1746
|
+
event.idempotency_key,
|
|
1747
|
+
event.event_type,
|
|
1748
|
+
json.dumps(payload, sort_keys=True),
|
|
1749
|
+
event.occurred_at,
|
|
1750
|
+
),
|
|
1751
|
+
)
|
|
1752
|
+
|
|
1753
|
+
def list_guard_events_v1(self, *, uploaded: bool | None = None, limit: int = 200) -> list[dict[str, object]]:
|
|
1754
|
+
query = """
|
|
1755
|
+
select event_id, idempotency_key, event_type, payload_json, occurred_at, uploaded_at
|
|
1756
|
+
from guard_cloud_events
|
|
1757
|
+
"""
|
|
1758
|
+
params: list[object] = []
|
|
1759
|
+
if uploaded is True:
|
|
1760
|
+
query += " where uploaded_at is not null"
|
|
1761
|
+
elif uploaded is False:
|
|
1762
|
+
query += " where uploaded_at is null"
|
|
1763
|
+
query += " order by occurred_at asc, event_id asc limit ?"
|
|
1764
|
+
params.append(limit)
|
|
1765
|
+
with self._connect() as connection:
|
|
1766
|
+
rows = connection.execute(query, tuple(params)).fetchall()
|
|
1767
|
+
events: list[dict[str, object]] = []
|
|
1768
|
+
for row in rows:
|
|
1769
|
+
payload = json.loads(str(row["payload_json"]))
|
|
1770
|
+
if not isinstance(payload, dict):
|
|
1771
|
+
payload = {}
|
|
1772
|
+
events.append(
|
|
1773
|
+
{
|
|
1774
|
+
"event_id": str(row["event_id"]),
|
|
1775
|
+
"idempotency_key": str(row["idempotency_key"]),
|
|
1776
|
+
"event_type": str(row["event_type"]),
|
|
1777
|
+
"occurred_at": str(row["occurred_at"]),
|
|
1778
|
+
"uploaded_at": row["uploaded_at"],
|
|
1779
|
+
"payload": payload,
|
|
1780
|
+
}
|
|
1781
|
+
)
|
|
1782
|
+
return events
|
|
1783
|
+
|
|
1784
|
+
def mark_guard_events_v1_uploaded(self, event_ids: list[str], uploaded_at: str) -> int:
|
|
1785
|
+
clean_ids = [event_id for event_id in event_ids if event_id.strip()]
|
|
1786
|
+
if not clean_ids:
|
|
1787
|
+
return 0
|
|
1788
|
+
placeholders = ", ".join("?" for _ in clean_ids)
|
|
1789
|
+
with self._connect() as connection:
|
|
1790
|
+
cursor = connection.execute(
|
|
1791
|
+
f"update guard_cloud_events set uploaded_at = ? where event_id in ({placeholders})",
|
|
1792
|
+
(uploaded_at, *clean_ids),
|
|
1793
|
+
)
|
|
1794
|
+
return int(cursor.rowcount)
|
|
1795
|
+
|
|
1701
1796
|
def add_event(self, event_name: str, payload: dict[str, object], now: str) -> None:
|
|
1702
1797
|
with self._connect() as connection:
|
|
1703
1798
|
connection.execute(
|
|
@@ -399,7 +399,11 @@ def test_claude_daemon_hook_command_falls_back_without_blocking_prompt_on_daemon
|
|
|
399
399
|
assert result.stderr == ""
|
|
400
400
|
payload = json.loads(result.stdout)
|
|
401
401
|
assert payload["hookSpecificOutput"]["hookEventName"] == "UserPromptSubmit"
|
|
402
|
-
assert "
|
|
402
|
+
assert "Do not ask for approval at the prompt stage" in payload["hookSpecificOutput"]["additionalContext"]
|
|
403
|
+
assert (
|
|
404
|
+
"route that concrete action into a HOL Guard approval question"
|
|
405
|
+
in payload["hookSpecificOutput"]["additionalContext"]
|
|
406
|
+
)
|
|
403
407
|
assert "Keep blocked" in payload["hookSpecificOutput"]["additionalContext"]
|
|
404
408
|
|
|
405
409
|
|