plugin-scanner 2.0.89__tar.gz → 2.0.91__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.89 → plugin_scanner-2.0.91}/PKG-INFO +1 -1
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/docs/guard/get-started.md +12 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/docs/guard/harness-support.md +5 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/docs/guard/testing-matrix.md +12 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/pyproject.toml +1 -1
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/pyproject.toml.bak +1 -1
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/guard/adapters/__init__.py +2 -0
- plugin_scanner-2.0.91/src/codex_plugin_scanner/guard/adapters/openclaw.py +183 -0
- plugin_scanner-2.0.91/src/codex_plugin_scanner/guard/adapters/openclaw_config.py +286 -0
- plugin_scanner-2.0.91/src/codex_plugin_scanner/guard/adapters/openclaw_support.py +396 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/guard/cli/commands.py +344 -10
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/version.py +1 -1
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/test_guard_runtime.py +323 -0
- plugin_scanner-2.0.91/tests/test_openclaw_adapter.py +369 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/.clusterfuzzlite/Dockerfile +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/.clusterfuzzlite/build.sh +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/.clusterfuzzlite/project.yaml +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/.clusterfuzzlite/requirements-atheris.txt +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/.dockerignore +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/.github/CODEOWNERS +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/.github/ISSUE_TEMPLATE/bug-report.yml +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/.github/ISSUE_TEMPLATE/feature-request.yml +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/.github/dependabot.yml +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/.github/workflows/ci.yml +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/.github/workflows/codeql.yml +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/.github/workflows/dependabot-uv-lock.yml +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/.github/workflows/fuzz.yml +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/.github/workflows/harness-smoke.yml +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/.github/workflows/publish.yml +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/.github/workflows/scorecard.yml +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/.gitignore +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/.pre-commit-hooks.yaml +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/CONTRIBUTING.md +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/Dockerfile +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/LICENSE +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/README.md +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/SECURITY.md +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/dashboard/index.html +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/dashboard/package.json +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/dashboard/pnpm-lock.yaml +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/dashboard/public/apple-touch-icon.png +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/dashboard/public/brand/Logo_Icon_Dark.png +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/dashboard/public/brand/Logo_Whole.png +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/dashboard/public/favicon-16x16.png +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/dashboard/public/favicon-32x32.png +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/dashboard/public/favicon.ico +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/dashboard/src/app.tsx +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/dashboard/src/approval-center-layout.tsx +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/dashboard/src/approval-center-primitives.tsx +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/dashboard/src/approval-center-utils.ts +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/dashboard/src/fleet-workspace.tsx +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/dashboard/src/guard-api.ts +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/dashboard/src/guard-demo.ts +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/dashboard/src/guard-types.ts +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/dashboard/src/main.tsx +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/dashboard/src/receipts-workspace.tsx +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/dashboard/src/runtime-overview.tsx +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/dashboard/src/settings-workspace.tsx +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/dashboard/src/styles.css +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/dashboard/src/vite-env.d.ts +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/dashboard/tsconfig.json +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/dashboard/vite.config.ts +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/docker-requirements.txt +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/docs/guard/approval-audit.md +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/docs/guard/architecture.md +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/docs/guard/local-vs-cloud.md +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/docs/trust/mcp-trust-draft.md +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/docs/trust/plugin-trust-draft.md +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/docs/trust/skill-trust-local.md +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/fuzzers/manifest_fuzzer.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/requirements.txt +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/schemas/plugin-quality.v1.json +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/schemas/scan-result.v1.json +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/schemas/verify-result.v1.json +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/__init__.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/action_runner.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/argparse_utils.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/checks/__init__.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/checks/best_practices.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/checks/claude.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/checks/code_quality.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/checks/ecosystem_common.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/checks/gemini.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/checks/manifest.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/checks/manifest_support.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/checks/marketplace.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/checks/mcp_security.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/checks/opencode.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/checks/operational_security.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/checks/security.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/checks/skill_security.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/cli.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/cli_ui.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/config.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/ecosystems/__init__.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/ecosystems/base.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/ecosystems/claude.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/ecosystems/codex.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/ecosystems/detect.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/ecosystems/gemini.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/ecosystems/opencode.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/ecosystems/registry.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/ecosystems/types.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/github_reporting.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/guard/__init__.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/guard/adapters/antigravity.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/guard/adapters/base.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/guard/adapters/claude_code.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/guard/adapters/codex.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/guard/adapters/copilot.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/guard/adapters/cursor.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/guard/adapters/gemini.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/guard/adapters/hermes.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/guard/adapters/mcp_servers.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/guard/adapters/opencode.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/guard/adapters/opencode_artifacts.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/guard/advisory_model.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/guard/approvals.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/guard/bridge/__init__.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/guard/capabilities.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/guard/cli/__init__.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/guard/cli/approval_commands.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/guard/cli/bootstrap.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/guard/cli/connect_flow.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/guard/cli/install_commands.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/guard/cli/product.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/guard/cli/prompt.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/guard/cli/render.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/guard/cli/update_commands.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/guard/codex_config.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/guard/config.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/guard/consumer/__init__.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/guard/consumer/service.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/guard/daemon/__init__.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/guard/daemon/client.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/guard/daemon/manager.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/guard/daemon/server.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/guard/daemon/static/apple-touch-icon.png +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/guard/daemon/static/assets/guard-dashboard.js +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/guard/daemon/static/assets/index.css +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/guard/daemon/static/brand/Logo_Icon_Dark.png +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/guard/daemon/static/brand/Logo_Whole.png +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/guard/daemon/static/favicon-16x16.png +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/guard/daemon/static/favicon-32x32.png +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/guard/daemon/static/favicon.ico +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/guard/daemon/static/index.html +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/guard/edge_events.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/guard/incident.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/guard/launcher.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/guard/mcp_tool_calls.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/guard/models.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/guard/policy/__init__.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/guard/policy/engine.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/guard/protect.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/guard/proxy/__init__.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/guard/proxy/remote.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/guard/proxy/runtime_mcp.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/guard/proxy/stdio.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/guard/receipts/__init__.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/guard/receipts/manager.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/guard/redaction.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/guard/risk.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/guard/runtime/__init__.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/guard/runtime/runner.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/guard/runtime/secret_file_requests.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/guard/runtime/surface_server.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/guard/schemas/__init__.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/guard/schemas/consumer_mode.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/guard/schemas/guard_event_v1.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/guard/schemas/surface_server.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/guard/shims.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/guard/store.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/guard/store_approvals.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/guard/store_connect.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/guard/types.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/integrations/__init__.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/integrations/cisco_mcp_scanner.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/integrations/cisco_skill_scanner.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/lint_fixes.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/marketplace_support.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/models.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/path_support.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/policy.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/quality_artifact.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/repo_detect.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/reporting.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/rules/__init__.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/rules/registry.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/rules/specs.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/scanner.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/submission.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/suppressions.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/trust_domain_scoring.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/trust_helpers.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/trust_mcp_scoring.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/trust_models.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/trust_plugin_scoring.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/trust_scoring.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/trust_skill_scoring.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/trust_specs.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/verification.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/__init__.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/conftest.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/fixtures/__init__.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/fixtures/bad-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/fixtures/bad-plugin/.mcp.json +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/fixtures/bad-plugin/secrets.js +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/fixtures/claude-plugin-good/.claude-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/fixtures/claude-plugin-good/LICENSE +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/fixtures/claude-plugin-good/README.md +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/fixtures/claude-plugin-good/SECURITY.md +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/fixtures/claude-plugin-good/hooks/hooks.json +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/fixtures/claude-plugin-good/skills/example/SKILL.md +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/fixtures/code-quality-bad/evil.js +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/fixtures/code-quality-bad/inject.js +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/fixtures/gemini-extension-good/GEMINI.md +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/fixtures/gemini-extension-good/LICENSE +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/fixtures/gemini-extension-good/README.md +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/fixtures/gemini-extension-good/SECURITY.md +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/fixtures/gemini-extension-good/commands/hello.toml +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/fixtures/gemini-extension-good/gemini-extension.json +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/fixtures/good-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/fixtures/good-plugin/.codexignore +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/fixtures/good-plugin/LICENSE +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/fixtures/good-plugin/README.md +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/fixtures/good-plugin/SECURITY.md +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/fixtures/good-plugin/assets/icon.svg +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/fixtures/good-plugin/assets/logo.svg +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/fixtures/good-plugin/assets/screenshot.svg +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/fixtures/good-plugin/skills/example/SKILL.md +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/fixtures/guard-codex-malicious-mcp/.codex/config.toml +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/fixtures/hermes-plugin-evil/config.yaml +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/fixtures/hermes-plugin-evil/mcp_servers.json +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/fixtures/hermes-plugin-evil/skills/security/malicious/SKILL.md +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/SKILL.md +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/references/api-setup.md +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/scripts/deploy.sh +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/fixtures/hermes-plugin-evil/skills/utils/benign/SKILL.md +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/fixtures/malformed-json/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/fixtures/malicious-skill-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/fixtures/malicious-skill-plugin/.codexignore +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/fixtures/malicious-skill-plugin/LICENSE +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/fixtures/malicious-skill-plugin/README.md +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/fixtures/malicious-skill-plugin/SECURITY.md +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/fixtures/malicious-skill-plugin/skills/leaky-skill/SKILL.md +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/fixtures/mcp-canary-server.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/fixtures/minimal-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/fixtures/missing-fields/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/fixtures/mit-license/LICENSE +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/fixtures/multi-ecosystem-repo/codex-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/fixtures/multi-ecosystem-repo/codex-plugin/LICENSE +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/fixtures/multi-ecosystem-repo/codex-plugin/README.md +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/fixtures/multi-ecosystem-repo/codex-plugin/SECURITY.md +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/fixtures/multi-ecosystem-repo/gemini-ext/README.md +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/fixtures/multi-ecosystem-repo/gemini-ext/gemini-extension.json +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/fixtures/multi-plugin-repo/.agents/plugins/marketplace.json +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/.codexignore +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/LICENSE +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/README.md +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/SECURITY.md +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/skills/example/SKILL.md +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/fixtures/multi-plugin-repo/plugins/beta-plugin/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/fixtures/multi-plugin-repo/plugins/beta-plugin/skills/example/SKILL.md +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/fixtures/no-version/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/fixtures/opencode-good/.opencode/commands/hello.md +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/fixtures/opencode-good/.opencode/plugins/example.ts +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/fixtures/opencode-good/LICENSE +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/fixtures/opencode-good/README.md +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/fixtures/opencode-good/SECURITY.md +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/fixtures/opencode-good/opencode.jsonc +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/fixtures/skills-missing-dir/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/fixtures/skills-no-frontmatter/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/fixtures/skills-no-frontmatter/skills/bad-skill/SKILL.md +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/fixtures/with-marketplace/.codex-plugin/plugin.json +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/fixtures/with-marketplace/marketplace-broken.json +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/fixtures/with-marketplace/marketplace.json +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/test-trust-scoring.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/test-trust-specs.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/test_action_runner.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/test_best_practices.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/test_cisco_install_surfaces.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/test_cli.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/test_code_quality.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/test_config.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/test_coverage_remaining.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/test_ecosystems.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/test_edge_cases.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/test_final_coverage.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/test_guard_approvals.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/test_guard_bootstrap.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/test_guard_capabilities.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/test_guard_claude_adapter.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/test_guard_cli.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/test_guard_codex_e2e.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/test_guard_codex_install.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/test_guard_codex_proxy.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/test_guard_config_paths.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/test_guard_connect_flow.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/test_guard_consumer_mode.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/test_guard_copilot_adapter.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/test_guard_copilot_proxy.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/test_guard_daemon_manager.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/test_guard_event_schema_v1.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/test_guard_events.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/test_guard_launch_env.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/test_guard_opencode_proxy.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/test_guard_product_flow.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/test_guard_protect.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/test_guard_render.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/test_guard_risk.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/test_guard_store_migrations.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/test_guard_surface_server.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/test_guard_verdicts.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/test_hermes_adapter.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/test_integration.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/test_lint_fixes.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/test_live_cisco_smoke.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/test_manifest.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/test_marketplace.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/test_mcp_security.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/test_operational_security.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/test_policy.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/test_quality_artifact.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/test_rule_registry.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/test_scanner.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/test_schema_contracts.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/test_security.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/test_security_ops.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/test_skill_security.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/test_submission.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/test_trust_scoring.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/test_trust_specs.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/test_verification.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/tests/test_versioning.py +0 -0
- {plugin_scanner-2.0.89 → plugin_scanner-2.0.91}/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.91
|
|
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
|
|
@@ -163,6 +163,15 @@ Hermes gets the normal Guard shim plus a Guard-owned bundle at `<guard-home>/her
|
|
|
163
163
|
Guard injects the managed overlay paths through `HERMES_GUARD_MCP_OVERLAY_PATH` and `HERMES_GUARD_PRETOOL_PATH` when
|
|
164
164
|
you launch Hermes through Guard.
|
|
165
165
|
|
|
166
|
+
OpenClaw gets the normal Guard shim plus a Guard-owned bundle at `<guard-home>/openclaw/` with:
|
|
167
|
+
|
|
168
|
+
- `overlay.json`
|
|
169
|
+
- `pretool-hook.json`
|
|
170
|
+
- `manifest.json`
|
|
171
|
+
|
|
172
|
+
Guard injects the managed overlay paths through `OPENCLAW_GUARD_OVERLAY_PATH` and `OPENCLAW_GUARD_PRETOOL_PATH` when
|
|
173
|
+
you launch OpenClaw through Guard. It does not mutate `~/.openclaw/openclaw.json`.
|
|
174
|
+
|
|
166
175
|
## Harness approval model
|
|
167
176
|
|
|
168
177
|
Guard uses three approval tiers:
|
|
@@ -189,6 +198,9 @@ Current strategy:
|
|
|
189
198
|
- `hermes`
|
|
190
199
|
prefers the managed Hermes same-channel path when Guard owns the overlay bundle, falls back to the approval center,
|
|
191
200
|
and keeps browser auto-open off for blocked requests
|
|
201
|
+
- `openclaw`
|
|
202
|
+
scans OpenClaw gateway config, channel posture, MCP servers, workspace skills, user skills, and OpenClaw-owned skills,
|
|
203
|
+
then prefers native-or-center delivery once the managed overlay bundle exists
|
|
192
204
|
- `gemini`
|
|
193
205
|
scans `.gemini/settings.json`, extension manifests, hooks, MCP registrations, and Gemini skill directories before
|
|
194
206
|
launch, then routes blocked changes to the approval center
|
|
@@ -42,6 +42,11 @@ Current Guard support in this repo:
|
|
|
42
42
|
- supports `hol-guard hermes bootstrap` and a Guard-managed Hermes overlay bundle under Guard home
|
|
43
43
|
- rewrites managed Hermes MCP entries through Guard’s existing proxy path and uses native-or-center delivery when the managed bundle is present
|
|
44
44
|
- blocks sensitive file reads and Docker-sensitive native pre-tool actions through the existing Guard hook path
|
|
45
|
+
- `openclaw`
|
|
46
|
+
- detects `~/.openclaw/openclaw.json`, gateway posture, channels, MCP servers, workspace skills, user skills, and OpenClaw-owned skills
|
|
47
|
+
- supports a Guard-managed OpenClaw overlay bundle under Guard home without mutating user OpenClaw config
|
|
48
|
+
- flags open DM channel posture and remote MCP endpoints before launch so chat-originated agent work can be reviewed
|
|
49
|
+
- uses native-or-center delivery when the managed bundle is present and keeps browser auto-open off for blocked requests
|
|
45
50
|
- `opencode`
|
|
46
51
|
- detects global and project config, MCP servers, config-defined commands, markdown commands, npm plugins, local
|
|
47
52
|
plugin files, and OpenCode-compatible skill directories
|
|
@@ -20,7 +20,9 @@ Manual verification should include:
|
|
|
20
20
|
- `hol-guard detect antigravity --json`
|
|
21
21
|
- `hol-guard detect gemini --json`
|
|
22
22
|
- `hol-guard detect opencode --json`
|
|
23
|
+
- `hol-guard detect openclaw --json`
|
|
23
24
|
- `hol-guard install opencode --json`
|
|
25
|
+
- `hol-guard install openclaw --json`
|
|
24
26
|
- `hol-guard update --dry-run --json`
|
|
25
27
|
- `hol-guard run cursor --dry-run --default-action allow --json`
|
|
26
28
|
- `hol-guard run gemini --dry-run --default-action allow --json`
|
|
@@ -36,6 +38,7 @@ Manual verification should include:
|
|
|
36
38
|
- `gemini --help`
|
|
37
39
|
- `opencode --help`
|
|
38
40
|
- `opencode run --help`
|
|
41
|
+
- `openclaw --help`
|
|
39
42
|
|
|
40
43
|
First-party canaries for local manual validation:
|
|
41
44
|
|
|
@@ -53,6 +56,15 @@ real `opencode` binary:
|
|
|
53
56
|
- a blocked prompt request queues an approval
|
|
54
57
|
- approving that request lets Guard hand off to `opencode run --dir <workspace> ...`
|
|
55
58
|
|
|
59
|
+
OpenClaw manual validation should include one isolated local gateway config where you prove all of the following against
|
|
60
|
+
the real `openclaw` binary:
|
|
61
|
+
|
|
62
|
+
- an open DM policy with wildcard senders is surfaced as a channel risk
|
|
63
|
+
- a remote MCP endpoint is surfaced as a network risk
|
|
64
|
+
- a newly added workspace skill blocks before launch when policy requires reapproval
|
|
65
|
+
- `hol-guard install openclaw` creates the managed overlay and pre-tool bundle under Guard home
|
|
66
|
+
- approving a blocked OpenClaw request resolves through native-or-center delivery without mutating user config
|
|
67
|
+
|
|
56
68
|
Nightly release-bar coverage should include:
|
|
57
69
|
|
|
58
70
|
- Codex on a self-hosted Linux runner
|
|
@@ -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.91"
|
|
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.91"
|
|
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.89 → plugin_scanner-2.0.91}/src/codex_plugin_scanner/guard/adapters/__init__.py
RENAMED
|
@@ -10,6 +10,7 @@ from .copilot import CopilotHarnessAdapter
|
|
|
10
10
|
from .cursor import CursorHarnessAdapter
|
|
11
11
|
from .gemini import GeminiHarnessAdapter
|
|
12
12
|
from .hermes import HermesHarnessAdapter
|
|
13
|
+
from .openclaw import OpenClawHarnessAdapter
|
|
13
14
|
from .opencode import OpenCodeHarnessAdapter
|
|
14
15
|
|
|
15
16
|
ADAPTERS: tuple[HarnessAdapter, ...] = (
|
|
@@ -20,6 +21,7 @@ ADAPTERS: tuple[HarnessAdapter, ...] = (
|
|
|
20
21
|
AntigravityHarnessAdapter(),
|
|
21
22
|
GeminiHarnessAdapter(),
|
|
22
23
|
HermesHarnessAdapter(),
|
|
24
|
+
OpenClawHarnessAdapter(),
|
|
23
25
|
OpenCodeHarnessAdapter(),
|
|
24
26
|
)
|
|
25
27
|
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
"""OpenClaw harness adapter."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
from ..models import GuardArtifact, HarnessDetection
|
|
9
|
+
from ..shims import install_guard_shim, remove_guard_shim
|
|
10
|
+
from .base import (
|
|
11
|
+
HarnessAdapter,
|
|
12
|
+
HarnessContext,
|
|
13
|
+
_command_available,
|
|
14
|
+
_ensure_path_within_root,
|
|
15
|
+
_json_payload,
|
|
16
|
+
_run_command_probe,
|
|
17
|
+
)
|
|
18
|
+
from .openclaw_config import load_config
|
|
19
|
+
from .openclaw_support import (
|
|
20
|
+
config_artifacts,
|
|
21
|
+
config_path,
|
|
22
|
+
install_state,
|
|
23
|
+
managed_root,
|
|
24
|
+
overlay_payload,
|
|
25
|
+
pretool_payload,
|
|
26
|
+
skill_artifacts,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
_OPENCLAW_MANAGED_APPROVAL_TIER = "native-or-center"
|
|
30
|
+
_OPENCLAW_MANAGED_PROMPT_CHANNEL = "native"
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class OpenClawHarnessAdapter(HarnessAdapter):
|
|
34
|
+
"""Discover OpenClaw gateway config, channels, MCP servers, and skills."""
|
|
35
|
+
|
|
36
|
+
harness = "openclaw"
|
|
37
|
+
executable = "openclaw"
|
|
38
|
+
approval_tier = "approval-center"
|
|
39
|
+
approval_summary = (
|
|
40
|
+
"Guard can scan OpenClaw gateway config, channels, skills, and MCP servers before agent sessions run."
|
|
41
|
+
)
|
|
42
|
+
fallback_hint = "Connect OpenClaw through the Guard-managed overlay or resolve requests in the approval center."
|
|
43
|
+
approval_prompt_channel = "native-fallback"
|
|
44
|
+
approval_auto_open_browser = False
|
|
45
|
+
|
|
46
|
+
def policy_path(self, context: HarnessContext) -> Path:
|
|
47
|
+
return config_path(context)
|
|
48
|
+
|
|
49
|
+
def detect(self, context: HarnessContext) -> HarnessDetection:
|
|
50
|
+
path = config_path(context)
|
|
51
|
+
payload = load_config(path)
|
|
52
|
+
found_paths: list[str] = []
|
|
53
|
+
artifacts: list[GuardArtifact] = []
|
|
54
|
+
if payload:
|
|
55
|
+
found_paths.append(str(path))
|
|
56
|
+
artifacts.extend(config_artifacts(context, path, payload))
|
|
57
|
+
artifacts.extend(skill_artifacts(context, payload, found_paths))
|
|
58
|
+
return HarnessDetection(
|
|
59
|
+
harness=self.harness,
|
|
60
|
+
installed=bool(found_paths) or _command_available(self.executable),
|
|
61
|
+
command_available=_command_available(self.executable),
|
|
62
|
+
config_paths=tuple(found_paths),
|
|
63
|
+
artifacts=tuple(artifacts),
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
def install(self, context: HarnessContext) -> dict[str, object]:
|
|
67
|
+
shim_manifest = install_guard_shim(self.harness, context)
|
|
68
|
+
root = managed_root(context)
|
|
69
|
+
manifest_path = root / "manifest.json"
|
|
70
|
+
overlay_path = root / "overlay.json"
|
|
71
|
+
pretool_path = root / "pretool-hook.json"
|
|
72
|
+
root.mkdir(parents=True, exist_ok=True)
|
|
73
|
+
existing_manifest = _json_payload(manifest_path)
|
|
74
|
+
state = install_state(
|
|
75
|
+
existing_manifest=existing_manifest,
|
|
76
|
+
overlay_path=overlay_path,
|
|
77
|
+
pretool_path=pretool_path,
|
|
78
|
+
)
|
|
79
|
+
detection = self.detect(context)
|
|
80
|
+
overlay_path.write_text(json.dumps(overlay_payload(detection), indent=2) + "\n", encoding="utf-8")
|
|
81
|
+
pretool_path.write_text(json.dumps(pretool_payload(context=context), indent=2) + "\n", encoding="utf-8")
|
|
82
|
+
manifest = {
|
|
83
|
+
"harness": self.harness,
|
|
84
|
+
"active": True,
|
|
85
|
+
"config_path": str(overlay_path),
|
|
86
|
+
**shim_manifest,
|
|
87
|
+
"install_state": state,
|
|
88
|
+
"managed_root": str(root),
|
|
89
|
+
"managed_manifest_path": str(manifest_path),
|
|
90
|
+
"managed_overlay_path": str(overlay_path),
|
|
91
|
+
"pretool_hook_path": str(pretool_path),
|
|
92
|
+
"capabilities": {
|
|
93
|
+
"same_channel": True,
|
|
94
|
+
"pretool": True,
|
|
95
|
+
"channel_posture": True,
|
|
96
|
+
"mcp_proxy": True,
|
|
97
|
+
},
|
|
98
|
+
"config_paths": list(detection.config_paths),
|
|
99
|
+
"artifact_count": len(detection.artifacts),
|
|
100
|
+
"notes": [
|
|
101
|
+
"Guard generated an OpenClaw overlay bundle for gateway posture and pre-tool protection.",
|
|
102
|
+
*[str(note) for note in shim_manifest.get("notes", [])],
|
|
103
|
+
],
|
|
104
|
+
}
|
|
105
|
+
manifest_path.write_text(json.dumps(manifest, indent=2) + "\n", encoding="utf-8")
|
|
106
|
+
return manifest
|
|
107
|
+
|
|
108
|
+
def uninstall(self, context: HarnessContext) -> dict[str, object]:
|
|
109
|
+
shim_manifest = remove_guard_shim(self.harness, context)
|
|
110
|
+
root = managed_root(context)
|
|
111
|
+
manifest = _json_payload(root / "manifest.json")
|
|
112
|
+
removed_paths: list[str] = []
|
|
113
|
+
for key in ("managed_manifest_path", "managed_overlay_path", "pretool_hook_path"):
|
|
114
|
+
value = manifest.get(key)
|
|
115
|
+
if not isinstance(value, str) or not value:
|
|
116
|
+
continue
|
|
117
|
+
path = Path(value)
|
|
118
|
+
_ensure_path_within_root(root, path, label=key)
|
|
119
|
+
if path.exists():
|
|
120
|
+
path.unlink()
|
|
121
|
+
removed_paths.append(str(path))
|
|
122
|
+
return {
|
|
123
|
+
"harness": self.harness,
|
|
124
|
+
"active": False,
|
|
125
|
+
"config_path": str(root / "overlay.json"),
|
|
126
|
+
**shim_manifest,
|
|
127
|
+
"removed_paths": removed_paths,
|
|
128
|
+
"notes": [
|
|
129
|
+
"Guard removed the managed OpenClaw overlay bundle and kept user OpenClaw config untouched.",
|
|
130
|
+
*[str(note) for note in shim_manifest.get("notes", [])],
|
|
131
|
+
],
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
def launch_environment(self, context: HarnessContext) -> dict[str, str]:
|
|
135
|
+
manifest = _json_payload(managed_root(context) / "manifest.json")
|
|
136
|
+
overlay_path = manifest.get("managed_overlay_path")
|
|
137
|
+
pretool_path = manifest.get("pretool_hook_path")
|
|
138
|
+
if not isinstance(overlay_path, str) or not isinstance(pretool_path, str):
|
|
139
|
+
return {}
|
|
140
|
+
return {
|
|
141
|
+
"OPENCLAW_GUARD_OVERLAY_PATH": overlay_path,
|
|
142
|
+
"OPENCLAW_GUARD_PRETOOL_PATH": pretool_path,
|
|
143
|
+
"OPENCLAW_GUARD_CHANNEL_POSTURE": "enabled",
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
def runtime_probe(self, context: HarnessContext) -> dict[str, object] | None:
|
|
147
|
+
manifest = _json_payload(managed_root(context) / "manifest.json")
|
|
148
|
+
overlay_path = manifest.get("managed_overlay_path")
|
|
149
|
+
pretool_path = manifest.get("pretool_hook_path")
|
|
150
|
+
return {
|
|
151
|
+
"command": _run_command_probe([self.executable, "--help"]) if _command_available(self.executable) else None,
|
|
152
|
+
"managed_install_present": bool(manifest),
|
|
153
|
+
"managed_install_ready": (
|
|
154
|
+
isinstance(overlay_path, str)
|
|
155
|
+
and Path(overlay_path).exists()
|
|
156
|
+
and isinstance(pretool_path, str)
|
|
157
|
+
and Path(pretool_path).exists()
|
|
158
|
+
),
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
def approval_flow(self, *, managed_install: dict[str, object] | None = None) -> dict[str, object]:
|
|
162
|
+
manifest = managed_install.get("manifest") if isinstance(managed_install, dict) else None
|
|
163
|
+
capabilities = manifest.get("capabilities") if isinstance(manifest, dict) else None
|
|
164
|
+
same_channel = isinstance(capabilities, dict) and bool(capabilities.get("same_channel"))
|
|
165
|
+
if same_channel:
|
|
166
|
+
return {
|
|
167
|
+
"tier": _OPENCLAW_MANAGED_APPROVAL_TIER,
|
|
168
|
+
"summary": (
|
|
169
|
+
"Guard uses OpenClaw native agent/channel delivery first and falls back to the approval center."
|
|
170
|
+
),
|
|
171
|
+
"fallback_hint": "Use the Guard approval center if OpenClaw cannot surface the pending request inline.",
|
|
172
|
+
"prompt_channel": _OPENCLAW_MANAGED_PROMPT_CHANNEL,
|
|
173
|
+
"auto_open_browser": False,
|
|
174
|
+
}
|
|
175
|
+
return {
|
|
176
|
+
"tier": "approval-center",
|
|
177
|
+
"summary": "Guard keeps OpenClaw approvals in the local approval center without forcing a browser open.",
|
|
178
|
+
"fallback_hint": (
|
|
179
|
+
"Resolve pending OpenClaw requests from the Guard approval center or `hol-guard approvals`."
|
|
180
|
+
),
|
|
181
|
+
"prompt_channel": "native-fallback",
|
|
182
|
+
"auto_open_browser": False,
|
|
183
|
+
}
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
"""OpenClaw configuration loading helpers."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import re
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
_MAX_INCLUDE_DEPTH = 8
|
|
10
|
+
_CONFIG_SUFFIXES = {".json", ".json5", ".yaml", ".yml"}
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def load_config(path: Path) -> dict[str, object]:
|
|
14
|
+
return _load_config(path, seen=set(), depth=0)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _load_config(path: Path, *, seen: set[Path], depth: int) -> dict[str, object]:
|
|
18
|
+
if depth > _MAX_INCLUDE_DEPTH:
|
|
19
|
+
return {}
|
|
20
|
+
try:
|
|
21
|
+
resolved_path = path.resolve()
|
|
22
|
+
except (OSError, RuntimeError):
|
|
23
|
+
return {}
|
|
24
|
+
if resolved_path in seen:
|
|
25
|
+
return {}
|
|
26
|
+
try:
|
|
27
|
+
raw = resolved_path.read_text(encoding="utf-8")
|
|
28
|
+
except (OSError, UnicodeDecodeError):
|
|
29
|
+
return {}
|
|
30
|
+
payload = _parse_config_payload(raw)
|
|
31
|
+
if not isinstance(payload, dict):
|
|
32
|
+
return {}
|
|
33
|
+
return _resolve_includes(resolved_path, payload, seen={*seen, resolved_path}, depth=depth)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _resolve_includes(path: Path, payload: dict[str, object], *, seen: set[Path], depth: int) -> dict[str, object]:
|
|
37
|
+
include_value = payload.get("$include")
|
|
38
|
+
include_paths = _include_paths(path.parent, include_value)
|
|
39
|
+
merged: dict[str, object] = {}
|
|
40
|
+
for include_path in include_paths:
|
|
41
|
+
included_payload = _load_config(include_path, seen=seen, depth=depth + 1)
|
|
42
|
+
merged = _deep_merge(merged, included_payload)
|
|
43
|
+
local_payload = {key: value for key, value in payload.items() if key != "$include"}
|
|
44
|
+
return _deep_merge(merged, local_payload)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _include_paths(base_dir: Path, value: object) -> tuple[Path, ...]:
|
|
48
|
+
values = value if isinstance(value, list) else [value]
|
|
49
|
+
paths: list[Path] = []
|
|
50
|
+
for item in values:
|
|
51
|
+
if not isinstance(item, str) or not item.strip():
|
|
52
|
+
continue
|
|
53
|
+
candidate = Path(item.strip()).expanduser()
|
|
54
|
+
path = candidate if candidate.is_absolute() else base_dir / candidate
|
|
55
|
+
if path.suffix.lower() in _CONFIG_SUFFIXES:
|
|
56
|
+
paths.append(path)
|
|
57
|
+
return tuple(paths)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def _deep_merge(base: dict[str, object], overlay: dict[str, object]) -> dict[str, object]:
|
|
61
|
+
result = dict(base)
|
|
62
|
+
for key, value in overlay.items():
|
|
63
|
+
existing = result.get(key)
|
|
64
|
+
if isinstance(existing, dict) and isinstance(value, dict):
|
|
65
|
+
result[key] = _deep_merge(existing, value)
|
|
66
|
+
else:
|
|
67
|
+
result[key] = value
|
|
68
|
+
return result
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def _parse_config_payload(raw: str) -> object:
|
|
72
|
+
try:
|
|
73
|
+
return json.loads(raw)
|
|
74
|
+
except json.JSONDecodeError:
|
|
75
|
+
pass
|
|
76
|
+
stripped = _strip_json_comments(raw)
|
|
77
|
+
normalized = _strip_trailing_json_commas(stripped)
|
|
78
|
+
try:
|
|
79
|
+
return json.loads(normalized)
|
|
80
|
+
except json.JSONDecodeError:
|
|
81
|
+
pass
|
|
82
|
+
json5_normalized = _strip_trailing_json_commas(
|
|
83
|
+
_convert_single_quoted_strings(_quote_unquoted_object_keys(stripped))
|
|
84
|
+
)
|
|
85
|
+
try:
|
|
86
|
+
return json.loads(json5_normalized)
|
|
87
|
+
except json.JSONDecodeError:
|
|
88
|
+
return {}
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def _strip_json_comments(text: str) -> str:
|
|
92
|
+
output: list[str] = []
|
|
93
|
+
quote: str | None = None
|
|
94
|
+
escape = False
|
|
95
|
+
in_line_comment = False
|
|
96
|
+
in_block_comment = False
|
|
97
|
+
index = 0
|
|
98
|
+
while index < len(text):
|
|
99
|
+
char = text[index]
|
|
100
|
+
next_char = text[index + 1] if index + 1 < len(text) else ""
|
|
101
|
+
if in_line_comment:
|
|
102
|
+
if char == "\n":
|
|
103
|
+
in_line_comment = False
|
|
104
|
+
output.append(char)
|
|
105
|
+
index += 1
|
|
106
|
+
continue
|
|
107
|
+
if in_block_comment:
|
|
108
|
+
if char == "*" and next_char == "/":
|
|
109
|
+
in_block_comment = False
|
|
110
|
+
index += 2
|
|
111
|
+
continue
|
|
112
|
+
if char == "\n":
|
|
113
|
+
output.append(char)
|
|
114
|
+
index += 1
|
|
115
|
+
continue
|
|
116
|
+
if quote is not None:
|
|
117
|
+
output.append(char)
|
|
118
|
+
if escape:
|
|
119
|
+
escape = False
|
|
120
|
+
elif char == "\\":
|
|
121
|
+
escape = True
|
|
122
|
+
elif char == quote:
|
|
123
|
+
quote = None
|
|
124
|
+
index += 1
|
|
125
|
+
continue
|
|
126
|
+
if char in {"'", '"'}:
|
|
127
|
+
quote = char
|
|
128
|
+
output.append(char)
|
|
129
|
+
index += 1
|
|
130
|
+
continue
|
|
131
|
+
if char == "/" and next_char == "/":
|
|
132
|
+
in_line_comment = True
|
|
133
|
+
index += 2
|
|
134
|
+
continue
|
|
135
|
+
if char == "/" and next_char == "*":
|
|
136
|
+
in_block_comment = True
|
|
137
|
+
index += 2
|
|
138
|
+
continue
|
|
139
|
+
output.append(char)
|
|
140
|
+
index += 1
|
|
141
|
+
return "".join(output)
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def _strip_trailing_json_commas(text: str) -> str:
|
|
145
|
+
output: list[str] = []
|
|
146
|
+
quote: str | None = None
|
|
147
|
+
escape = False
|
|
148
|
+
index = 0
|
|
149
|
+
while index < len(text):
|
|
150
|
+
char = text[index]
|
|
151
|
+
if quote is not None:
|
|
152
|
+
output.append(char)
|
|
153
|
+
if escape:
|
|
154
|
+
escape = False
|
|
155
|
+
elif char == "\\":
|
|
156
|
+
escape = True
|
|
157
|
+
elif char == quote:
|
|
158
|
+
quote = None
|
|
159
|
+
index += 1
|
|
160
|
+
continue
|
|
161
|
+
if char in {"'", '"'}:
|
|
162
|
+
quote = char
|
|
163
|
+
output.append(char)
|
|
164
|
+
index += 1
|
|
165
|
+
continue
|
|
166
|
+
if char == ",":
|
|
167
|
+
lookahead = index + 1
|
|
168
|
+
while lookahead < len(text) and text[lookahead] in " \t\r\n":
|
|
169
|
+
lookahead += 1
|
|
170
|
+
if lookahead < len(text) and text[lookahead] in "}]":
|
|
171
|
+
index += 1
|
|
172
|
+
continue
|
|
173
|
+
output.append(char)
|
|
174
|
+
index += 1
|
|
175
|
+
return "".join(output)
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def _quote_unquoted_object_keys(text: str) -> str:
|
|
179
|
+
output: list[str] = []
|
|
180
|
+
quote: str | None = None
|
|
181
|
+
escape = False
|
|
182
|
+
expecting_key = False
|
|
183
|
+
index = 0
|
|
184
|
+
while index < len(text):
|
|
185
|
+
char = text[index]
|
|
186
|
+
if quote is not None:
|
|
187
|
+
output.append(char)
|
|
188
|
+
if escape:
|
|
189
|
+
escape = False
|
|
190
|
+
elif char == "\\":
|
|
191
|
+
escape = True
|
|
192
|
+
elif char == quote:
|
|
193
|
+
quote = None
|
|
194
|
+
index += 1
|
|
195
|
+
continue
|
|
196
|
+
if char in {"'", '"'}:
|
|
197
|
+
quote = char
|
|
198
|
+
output.append(char)
|
|
199
|
+
index += 1
|
|
200
|
+
continue
|
|
201
|
+
if char in "{,":
|
|
202
|
+
expecting_key = True
|
|
203
|
+
output.append(char)
|
|
204
|
+
index += 1
|
|
205
|
+
continue
|
|
206
|
+
if expecting_key and char.isspace():
|
|
207
|
+
output.append(char)
|
|
208
|
+
index += 1
|
|
209
|
+
continue
|
|
210
|
+
if expecting_key and _starts_identifier(char):
|
|
211
|
+
match = re.match(r"[A-Za-z_$][A-Za-z0-9_$-]*", text[index:])
|
|
212
|
+
if match is not None:
|
|
213
|
+
key = match.group(0)
|
|
214
|
+
lookahead = index + len(key)
|
|
215
|
+
while lookahead < len(text) and text[lookahead].isspace():
|
|
216
|
+
lookahead += 1
|
|
217
|
+
if lookahead < len(text) and text[lookahead] == ":":
|
|
218
|
+
output.append(json.dumps(key))
|
|
219
|
+
index += len(key)
|
|
220
|
+
expecting_key = False
|
|
221
|
+
continue
|
|
222
|
+
expecting_key = False
|
|
223
|
+
output.append(char)
|
|
224
|
+
index += 1
|
|
225
|
+
return "".join(output)
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
def _convert_single_quoted_strings(text: str) -> str:
|
|
229
|
+
output: list[str] = []
|
|
230
|
+
quote: str | None = None
|
|
231
|
+
escape = False
|
|
232
|
+
index = 0
|
|
233
|
+
while index < len(text):
|
|
234
|
+
char = text[index]
|
|
235
|
+
if quote == "'":
|
|
236
|
+
if escape:
|
|
237
|
+
_append_single_quote_escape(output, char)
|
|
238
|
+
escape = False
|
|
239
|
+
elif char == "\\":
|
|
240
|
+
escape = True
|
|
241
|
+
elif char == "'":
|
|
242
|
+
quote = None
|
|
243
|
+
output.append('"')
|
|
244
|
+
elif char == '"':
|
|
245
|
+
output.append('\\"')
|
|
246
|
+
else:
|
|
247
|
+
output.append(char)
|
|
248
|
+
index += 1
|
|
249
|
+
continue
|
|
250
|
+
if quote == '"':
|
|
251
|
+
output.append(char)
|
|
252
|
+
if escape:
|
|
253
|
+
escape = False
|
|
254
|
+
elif char == "\\":
|
|
255
|
+
escape = True
|
|
256
|
+
elif char == '"':
|
|
257
|
+
quote = None
|
|
258
|
+
index += 1
|
|
259
|
+
continue
|
|
260
|
+
if char == "'":
|
|
261
|
+
quote = "'"
|
|
262
|
+
output.append('"')
|
|
263
|
+
index += 1
|
|
264
|
+
continue
|
|
265
|
+
if char == '"':
|
|
266
|
+
quote = '"'
|
|
267
|
+
output.append(char)
|
|
268
|
+
index += 1
|
|
269
|
+
return "".join(output)
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
def _append_single_quote_escape(output: list[str], char: str) -> None:
|
|
273
|
+
if char == "'":
|
|
274
|
+
output.append("'")
|
|
275
|
+
elif char == '"':
|
|
276
|
+
output.append('\\"')
|
|
277
|
+
elif char == "\\":
|
|
278
|
+
output.append("\\\\")
|
|
279
|
+
elif char in {"b", "f", "n", "r", "t", "/"}:
|
|
280
|
+
output.append(f"\\{char}")
|
|
281
|
+
else:
|
|
282
|
+
output.append(char)
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
def _starts_identifier(char: str) -> bool:
|
|
286
|
+
return char.isalpha() or char in {"_", "$"}
|