failproofai 1.0.1 → 1.0.2-beta.1
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.
- package/.next/standalone/.next/BUILD_ID +1 -1
- package/.next/standalone/.next/build-manifest.json +3 -3
- package/.next/standalone/.next/prerender-manifest.json +6 -6
- package/.next/standalone/.next/required-server-files.json +1 -1
- package/.next/standalone/.next/server/app/_global-error/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/_global-error/page.js +2 -2
- package/.next/standalone/.next/server/app/_global-error/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/_global-error.html +1 -1
- package/.next/standalone/.next/server/app/_global-error.rsc +7 -7
- package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +6 -6
- package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +7 -7
- package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/_not-found/page.js +2 -2
- package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/_not-found.html +1 -1
- package/.next/standalone/.next/server/app/_not-found.rsc +15 -15
- package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +15 -15
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +14 -14
- package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/api/audit/invite/route.js +3 -2
- package/.next/standalone/.next/server/app/api/audit/invite/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/audit/run/route.js +5 -4
- package/.next/standalone/.next/server/app/api/audit/run/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/audit/status/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/login-request/route.js +2 -2
- package/.next/standalone/.next/server/app/api/auth/login-request/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/login-verify/route.js +4 -3
- package/.next/standalone/.next/server/app/api/auth/login-verify/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/logout/route.js +4 -4
- package/.next/standalone/.next/server/app/api/auth/logout/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/status/route.js +4 -4
- package/.next/standalone/.next/server/app/api/auth/status/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/download/[project]/[session]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/audit/page/server-reference-manifest.json +2 -2
- package/.next/standalone/.next/server/app/audit/page.js +2 -2
- package/.next/standalone/.next/server/app/audit/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/audit/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/index.html +1 -1
- package/.next/standalone/.next/server/app/index.rsc +15 -15
- package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +14 -14
- package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +15 -15
- package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/page.js +2 -2
- package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/policies/page/server-reference-manifest.json +66 -22
- package/.next/standalone/.next/server/app/policies/page.js +5 -4
- package/.next/standalone/.next/server/app/policies/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/policies/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/project/[name]/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/project/[name]/page.js +2 -2
- package/.next/standalone/.next/server/app/project/[name]/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/project/[name]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/react-loadable-manifest.json +2 -2
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/server-reference-manifest.json +2 -2
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js +2 -2
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/projects/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/projects/page.js +2 -2
- package/.next/standalone/.next/server/app/projects/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/projects/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/settings/page/server-reference-manifest.json +4 -4
- package/.next/standalone/.next/server/app/settings/page.js +1 -1
- package/.next/standalone/.next/server/app/settings/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/settings/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/chunks/{[externals]__1qzqcio._.js → [externals]__0e32-7c._.js} +1 -1
- package/.next/standalone/.next/server/chunks/[externals]__1s462yl._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__01eqny5._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0_0xu5z._.js +1 -1
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__1p8poxd._.js → [root-of-the-server]__0igxt32._.js} +2 -2
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0neqlu-._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0o07qi9._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0t9oqnn._.js +3 -0
- package/.next/standalone/.next/server/chunks/_0bs-v8p._.js +3 -0
- package/.next/standalone/.next/server/chunks/_0tovk6q._.js +3 -0
- package/.next/standalone/.next/server/chunks/_0trp3yc._.js +3 -0
- package/.next/standalone/.next/server/chunks/_185gyk9._.js +3 -0
- package/.next/standalone/.next/server/chunks/_1ek68ln._.js +5 -6
- package/.next/standalone/.next/server/chunks/lib_telemetry-id_ts_1k8gj9o._.js +3 -0
- package/.next/standalone/.next/server/chunks/{node_modules_posthog-node_dist_entrypoints_index_node_mjs_09d5c79._.js → node_modules_posthog-node_dist_entrypoints_index_node_mjs_04op_ou._.js} +1 -1
- package/.next/standalone/.next/server/chunks/{node_modules_posthog-node_dist_entrypoints_index_node_mjs_1d2g73o._.js → node_modules_posthog-node_dist_entrypoints_index_node_mjs_0dr20uz._.js} +1 -1
- package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_1nxcc4v._.js +1 -1
- package/.next/standalone/.next/server/chunks/src_hooks_0xbw3bu._.js +12 -0
- package/.next/standalone/.next/server/chunks/src_hooks_fp-home_ts_09kv0bn._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__013jr2b._.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__01wy8d-._.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__02npjtd._.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0spkm68._.js → [root-of-the-server]__07gm3zl._.js} +21 -20
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0da85px._.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0ftmoxc._.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0l4748x._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0oa1lav._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0p-5p8u._.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0s740oi._.js +6 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__192nhvh._.js +5 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1p2otjt._.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/_08x1r5t._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_0bwu160._.js +23 -0
- package/.next/standalone/.next/server/chunks/ssr/{_0u5zauv._.js → _0ns6k3a._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_1u8-lu2._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_1zopuov._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_audit__components_audit-dashboard_tsx_0p9ud47._.js +13 -13
- package/.next/standalone/.next/server/chunks/ssr/app_global-error_tsx_1kp6l3x._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_policies_hooks-client_tsx_19dqvpc._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/app_settings_settings-client_tsx_20lq-mq._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_08o7bqo._.js +10 -0
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_builtin-policies_ts_09j2ndl._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_fp-home_ts_0je3xkv._.js +1 -1
- package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
- package/.next/standalone/.next/server/middleware.js +1 -1
- package/.next/standalone/.next/server/pages/404.html +1 -1
- package/.next/standalone/.next/server/pages/500.html +1 -1
- package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/server-reference-manifest.json +71 -27
- package/.next/standalone/.next/static/chunks/{3c808qrfcq0im.js → 01u1cf8js2yd6.js} +1 -1
- package/.next/standalone/.next/static/chunks/{0k5050ezjvv49.js → 0izgu7nqh2zp-.js} +1 -1
- package/.next/standalone/.next/static/chunks/0u4-org_e7zde.js +1 -0
- package/.next/standalone/.next/static/chunks/1mfzsja6ftim5.css +2 -0
- package/.next/standalone/.next/static/chunks/{2zpp1we2jv6tb.js → 1tynnrrh711lr.js} +1 -1
- package/.next/standalone/.next/static/chunks/{1ide1wl2f7zi1.js → 22rqvlr7p_1mv.js} +13 -13
- package/.next/standalone/.next/static/chunks/{31s1smp-g7gks.js → 2whxqyskpirbk.js} +1 -1
- package/.next/standalone/.next/static/chunks/31mwk8k0q3_3g.js +6 -0
- package/.next/standalone/.next/static/chunks/3ms6gxfm82t6s.js +1 -0
- package/.next/standalone/.next/static/chunks/{37fcpc36x9gpe.js → 3tbbs_d8xrvyp.js} +1 -1
- package/.next/standalone/.next/static/chunks/43nlc236oo898.js +1 -0
- package/.next/standalone/SECURITY.md +5 -4
- package/.next/standalone/app/actions/get-hooks-config.ts +108 -17
- package/.next/standalone/app/actions/pack-actions.ts +163 -0
- package/.next/standalone/app/audit/_components/run-progress.tsx +1 -1
- package/.next/standalone/app/audit/_components/share-templates.ts +13 -13
- package/.next/standalone/app/policies/hooks-client.tsx +320 -15
- package/.next/standalone/components/navbar.tsx +2 -28
- package/.next/standalone/components/reach-developers.tsx +8 -1
- package/.next/standalone/docs-old/agenteye/python-sdk-skill.mdx +1 -1
- package/.next/standalone/docs-old/agenteye/python-sdk.mdx +59 -35
- package/.next/standalone/docs-old/i18n/README.ar.md +1 -0
- package/.next/standalone/docs-old/i18n/README.de.md +1 -0
- package/.next/standalone/docs-old/i18n/README.es.md +1 -0
- package/.next/standalone/docs-old/i18n/README.fr.md +1 -0
- package/.next/standalone/docs-old/i18n/README.he.md +1 -0
- package/.next/standalone/docs-old/i18n/README.hi.md +1 -0
- package/.next/standalone/docs-old/i18n/README.it.md +1 -0
- package/.next/standalone/docs-old/i18n/README.ja.md +1 -0
- package/.next/standalone/docs-old/i18n/README.ko.md +1 -0
- package/.next/standalone/docs-old/i18n/README.pt-br.md +1 -0
- package/.next/standalone/docs-old/i18n/README.ru.md +1 -0
- package/.next/standalone/docs-old/i18n/README.tr.md +1 -0
- package/.next/standalone/docs-old/i18n/README.vi.md +1 -0
- package/.next/standalone/docs-old/i18n/README.zh.md +1 -0
- package/.next/standalone/fp-cloud-cli/CHANGELOG.md +431 -0
- package/.next/standalone/fp-cloud-cli/LICENSE +42 -0
- package/.next/standalone/fp-cloud-cli/MANIFEST.in +10 -0
- package/.next/standalone/fp-cloud-cli/README.md +259 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/__init__.py +10 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/__main__.py +4 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/_click_compat.py +64 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/_context.py +332 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/_version.py +1 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/analytics.py +432 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/analytics_config.py +77 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/analytics_registry.py +83 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/app.py +492 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/auth.py +160 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/client.py +1694 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/__init__.py +0 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/_write.py +214 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/agent_cmds.py +407 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/alerts_cmds.py +445 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/audits_cmds.py +1054 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/auth_cmds.py +512 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/errors_cmds.py +190 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/evals_cmds.py +161 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/events_cmds.py +159 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/fleet_cmds.py +416 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/guardrails_cmds.py +148 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/incidents_cmds.py +472 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/keys_cmds.py +407 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/list_cmds.py +63 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/orgs_cmds.py +319 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/policies_cmds.py +499 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/queries_cmds.py +378 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/sessions_cmds.py +151 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/settings_cmds.py +150 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/usage_cmds.py +35 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/users_cmds.py +404 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/config.py +330 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/dates.py +78 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/enforcement.py +345 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/errors.py +98 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/models.py +891 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/orgs.py +30 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/output.py +6593 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/permissions.py +208 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/policy_check.py +290 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/py.typed +0 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/select.py +322 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/theme.py +53 -0
- package/.next/standalone/fp-cloud-cli/pyproject.toml +106 -0
- package/.next/standalone/fp-cloud-cli/skill/SKILL.md +302 -0
- package/.next/standalone/fp-cloud-cli/skill/agents/openai.yaml +8 -0
- package/.next/standalone/fp-cloud-cli/skill/references/commands.md +254 -0
- package/.next/standalone/fp-cloud-cli/tests/__init__.py +0 -0
- package/.next/standalone/fp-cloud-cli/tests/conftest.py +92 -0
- package/.next/standalone/fp-cloud-cli/tests/test_alerting.py +644 -0
- package/.next/standalone/fp-cloud-cli/tests/test_analytics.py +385 -0
- package/.next/standalone/fp-cloud-cli/tests/test_audits.py +950 -0
- package/.next/standalone/fp-cloud-cli/tests/test_auth.py +98 -0
- package/.next/standalone/fp-cloud-cli/tests/test_auth_mode.py +272 -0
- package/.next/standalone/fp-cloud-cli/tests/test_click_compat.py +118 -0
- package/.next/standalone/fp-cloud-cli/tests/test_client.py +299 -0
- package/.next/standalone/fp-cloud-cli/tests/test_commands.py +814 -0
- package/.next/standalone/fp-cloud-cli/tests/test_config.py +99 -0
- package/.next/standalone/fp-cloud-cli/tests/test_dashboards_agent.py +340 -0
- package/.next/standalone/fp-cloud-cli/tests/test_dates.py +76 -0
- package/.next/standalone/fp-cloud-cli/tests/test_enforcement_logic.py +481 -0
- package/.next/standalone/fp-cloud-cli/tests/test_facets.py +170 -0
- package/.next/standalone/fp-cloud-cli/tests/test_failproofai_home.py +722 -0
- package/.next/standalone/fp-cloud-cli/tests/test_fp_home_contract.py +198 -0
- package/.next/standalone/fp-cloud-cli/tests/test_hardening.py +186 -0
- package/.next/standalone/fp-cloud-cli/tests/test_help_table_coverage.py +127 -0
- package/.next/standalone/fp-cloud-cli/tests/test_keys_queries.py +496 -0
- package/.next/standalone/fp-cloud-cli/tests/test_list.py +59 -0
- package/.next/standalone/fp-cloud-cli/tests/test_multivalue.py +183 -0
- package/.next/standalone/fp-cloud-cli/tests/test_no_customer_identifiers.py +168 -0
- package/.next/standalone/fp-cloud-cli/tests/test_operator.py +492 -0
- package/.next/standalone/fp-cloud-cli/tests/test_orgs.py +692 -0
- package/.next/standalone/fp-cloud-cli/tests/test_output.py +1732 -0
- package/.next/standalone/fp-cloud-cli/tests/test_policy_check.py +191 -0
- package/.next/standalone/fp-cloud-cli/tests/test_readme_matches_reality.py +125 -0
- package/.next/standalone/fp-cloud-cli/tests/test_review_fixes.py +161 -0
- package/.next/standalone/fp-cloud-cli/tests/test_telemetry_completeness.py +90 -0
- package/.next/standalone/fp-cloud-cli/tests/test_usage.py +115 -0
- package/.next/standalone/fp-cloud-cli/tests/test_v1_origin_diagnostic.py +70 -0
- package/.next/standalone/fp-cloud-cli/tests/test_v1_routing.py +214 -0
- package/.next/standalone/fp-cloud-cli/tests/test_whoami.py +104 -0
- package/.next/standalone/fp-cloud-cli/uv.lock +552 -0
- package/.next/standalone/package.json +27 -21
- package/.next/standalone/sdk/python/CHANGELOG.md +70 -0
- package/.next/standalone/sdk/python/LICENSE +42 -0
- package/.next/standalone/sdk/python/MANIFEST.in +21 -0
- package/.next/standalone/sdk/python/README.md +466 -0
- package/.next/standalone/sdk/python/docs/README.md +249 -0
- package/.next/standalone/sdk/python/docs/_shared/README.md +43 -0
- package/.next/standalone/sdk/python/docs/_shared/__init__.py +195 -0
- package/.next/standalone/sdk/python/docs/crewai/README.md +328 -0
- package/.next/standalone/sdk/python/docs/crewai/examples/quickstart.py +61 -0
- package/.next/standalone/sdk/python/docs/crewai/examples/research_crew.py +92 -0
- package/.next/standalone/sdk/python/docs/langgraph/README.md +481 -0
- package/.next/standalone/sdk/python/docs/langgraph/examples/quickstart.py +51 -0
- package/.next/standalone/sdk/python/docs/langgraph/examples/research_agent.py +86 -0
- package/.next/standalone/sdk/python/docs/langgraph/examples/supervisor_handoff.py +83 -0
- package/.next/standalone/sdk/python/docs/llama_index/README.md +332 -0
- package/.next/standalone/sdk/python/docs/llama_index/examples/quickstart.py +62 -0
- package/.next/standalone/sdk/python/docs/llama_index/examples/research_agent.py +93 -0
- package/.next/standalone/sdk/python/docs/manual/README.md +491 -0
- package/.next/standalone/sdk/python/docs/manual/examples/quickstart.py +57 -0
- package/.next/standalone/sdk/python/docs/manual/examples/research_agent.py +133 -0
- package/.next/standalone/sdk/python/docs/pydantic_ai/README.md +323 -0
- package/.next/standalone/sdk/python/docs/pydantic_ai/examples/quickstart.py +48 -0
- package/.next/standalone/sdk/python/docs/pydantic_ai/examples/research_agent.py +94 -0
- package/.next/standalone/sdk/python/failproofai_sdk/__init__.py +131 -0
- package/.next/standalone/sdk/python/failproofai_sdk/_context.py +218 -0
- package/.next/standalone/sdk/python/failproofai_sdk/_environment.py +78 -0
- package/.next/standalone/sdk/python/failproofai_sdk/_events.py +752 -0
- package/.next/standalone/sdk/python/failproofai_sdk/_resolver.py +118 -0
- package/.next/standalone/sdk/python/failproofai_sdk/_runtime.py +21 -0
- package/.next/standalone/sdk/python/failproofai_sdk/_schema.py +317 -0
- package/.next/standalone/sdk/python/failproofai_sdk/_scopes.py +422 -0
- package/.next/standalone/sdk/python/failproofai_sdk/_version.py +1 -0
- package/.next/standalone/sdk/python/failproofai_sdk/_writer.py +750 -0
- package/.next/standalone/sdk/python/failproofai_sdk/integrations/__init__.py +274 -0
- package/.next/standalone/sdk/python/failproofai_sdk/integrations/_compat.py +247 -0
- package/.next/standalone/sdk/python/failproofai_sdk/integrations/_core.py +1076 -0
- package/.next/standalone/sdk/python/failproofai_sdk/integrations/crewai.py +1669 -0
- package/.next/standalone/sdk/python/failproofai_sdk/integrations/langchain.py +2151 -0
- package/.next/standalone/sdk/python/failproofai_sdk/integrations/llama_index.py +1588 -0
- package/.next/standalone/sdk/python/failproofai_sdk/integrations/pydantic_ai.py +977 -0
- package/.next/standalone/sdk/python/failproofai_sdk/py.typed +0 -0
- package/.next/standalone/sdk/python/pyproject.toml +92 -0
- package/.next/standalone/sdk/python/skill/SKILL.md +399 -0
- package/.next/standalone/sdk/python/skill/agents/openai.yaml +8 -0
- package/.next/standalone/sdk/python/skill/references/events.md +234 -0
- package/.next/standalone/sdk/python/skill/references/frameworks.md +314 -0
- package/.next/standalone/sdk/python/skill/references/install.md +100 -0
- package/.next/standalone/sdk/python/skill/references/integration.md +352 -0
- package/.next/standalone/sdk/python/tests/__init__.py +0 -0
- package/.next/standalone/sdk/python/tests/conftest.py +169 -0
- package/.next/standalone/sdk/python/tests/integrations/__init__.py +6 -0
- package/.next/standalone/sdk/python/tests/integrations/test_crewai.py +1299 -0
- package/.next/standalone/sdk/python/tests/integrations/test_langchain.py +2338 -0
- package/.next/standalone/sdk/python/tests/integrations/test_llama_index.py +1576 -0
- package/.next/standalone/sdk/python/tests/integrations/test_pydantic_ai.py +1102 -0
- package/.next/standalone/sdk/python/tests/test_context.py +410 -0
- package/.next/standalone/sdk/python/tests/test_docs.py +411 -0
- package/.next/standalone/sdk/python/tests/test_durability.py +1352 -0
- package/.next/standalone/sdk/python/tests/test_encoding.py +571 -0
- package/.next/standalone/sdk/python/tests/test_integrations.py +1127 -0
- package/.next/standalone/sdk/python/tests/test_no_customer_identifiers.py +214 -0
- package/.next/standalone/sdk/python/tests/test_packaging.py +185 -0
- package/.next/standalone/sdk/python/tests/test_resolver_umbrella.py +178 -0
- package/.next/standalone/sdk/python/tests/test_scopes.py +487 -0
- package/.next/standalone/sdk/python/tests/test_sdk.py +923 -0
- package/.next/standalone/sdk/python/tests/test_server_contract.py +650 -0
- package/.next/standalone/sdk/python/tests/test_site_docs.py +371 -0
- package/.next/standalone/sdk/python/tests/test_skill_snippets.py +165 -0
- package/.next/standalone/sdk/python/tests/test_spool_contract.py +287 -0
- package/.next/standalone/sdk/python/tests/test_spool_creation.py +360 -0
- package/.next/standalone/sdk/python/tests/test_wire_format.py +279 -0
- package/.next/standalone/sdk/python/tests/test_zero_dependencies.py +378 -0
- package/.next/standalone/sdk/python/uv.lock +6005 -0
- package/.next/standalone/server.js +1 -1
- package/README.md +77 -21
- package/bin/failproofai.mjs +579 -306
- package/dist/cli.mjs +6499 -3662
- package/dist/index.js +5 -5
- package/dist/worker.mjs +2390 -1978
- package/package.json +27 -21
- package/scripts/build-policy-pack.mjs +121 -0
- package/scripts/changelog-section.py +77 -0
- package/scripts/prune-standalone.mjs +3 -0
- package/scripts/python-version.py +232 -0
- package/scripts/translate-docs/cli.ts +21 -0
- package/scripts/translate-docs/mintlify-nav.ts +85 -13
- package/scripts/translate-docs/translator.ts +66 -11
- package/src/audit/cache.ts +40 -3
- package/src/audit/cli.ts +1 -1
- package/src/audit/index.ts +1 -1
- package/src/audit/replay.ts +89 -3
- package/src/audit/report.ts +5 -5
- package/src/audit/schedule-cli.ts +49 -53
- package/src/hooks/builtin-policies.ts +217 -579
- package/src/hooks/cloud-enrollment-cli.ts +66 -27
- package/src/hooks/cloud-managed-policies.ts +6 -1
- package/src/hooks/configure-wizard.ts +56 -369
- package/src/hooks/custom-hooks-loader.ts +89 -6
- package/src/hooks/fp-home.ts +92 -4
- package/src/hooks/fp-reset.ts +34 -2
- package/src/hooks/handler.ts +181 -11
- package/src/hooks/harness-cli.ts +79 -25
- package/src/hooks/hook-activity-store.ts +13 -3
- package/src/hooks/install-prompt.ts +25 -3
- package/src/hooks/manager.ts +424 -172
- package/src/hooks/migrations.ts +2 -2
- package/src/hooks/pack-cli.ts +885 -0
- package/src/hooks/pack-failclosed.ts +189 -0
- package/src/hooks/pack-manifest.ts +352 -0
- package/src/hooks/pack-store.ts +891 -0
- package/src/hooks/policy-catalog.ts +535 -0
- package/src/hooks/policy-evaluator.ts +24 -16
- package/src/hooks/policy-registry.ts +8 -2
- package/src/hooks/policy-types.ts +26 -0
- package/src/hooks/session-pause-cli.ts +33 -9
- package/src/hooks/tui.ts +695 -39
- package/src/hooks/types.ts +15 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0_usx0j._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0_zk340._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0agdw2a._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0d1u_na._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0oy4kxb._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0zf034w._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1tvn4wf._.js +0 -3
- package/.next/standalone/.next/server/chunks/_0lxbzdq._.js +0 -3
- package/.next/standalone/.next/server/chunks/_1ger0a2._.js +0 -3
- package/.next/standalone/.next/server/chunks/_1zuiiy3._.js +0 -3
- package/.next/standalone/.next/server/chunks/node_modules_posthog-node_dist_entrypoints_index_node_mjs_1iiwm5e._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__07tbbf3._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0kc8_v5._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0kgy9da._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0mhqk5a._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0pltpn8._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0qkcyf-._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0w0x_ok._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1_dfocn._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1bfex62._.js +0 -5
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1kzapg5._.js +0 -6
- package/.next/standalone/.next/server/chunks/ssr/_0lvyuv4._.js +0 -23
- package/.next/standalone/.next/static/chunks/2_2lhuojq2xop.js +0 -1
- package/.next/standalone/.next/static/chunks/36fw6yk1s6x_f.js +0 -1
- package/.next/standalone/.next/static/chunks/3k7mxbp9hiom0.js +0 -1
- package/.next/standalone/.next/static/chunks/3tzq0260n17ae.js +0 -6
- package/.next/standalone/.next/static/chunks/3wq9x0c-7ld5d.css +0 -2
- package/src/hooks/policy-presets.ts +0 -135
- /package/.next/standalone/.next/static/{ZklXxyA5ygsWSQz2u_zii → I5kEb9kSgYS63j4iqfVLO}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{ZklXxyA5ygsWSQz2u_zii → I5kEb9kSgYS63j4iqfVLO}/_clientMiddlewareManifest.js +0 -0
- /package/.next/standalone/.next/static/{ZklXxyA5ygsWSQz2u_zii → I5kEb9kSgYS63j4iqfVLO}/_ssgManifest.js +0 -0
|
@@ -6,7 +6,13 @@ import { statSync } from "node:fs";
|
|
|
6
6
|
import { readFile, writeFile } from "node:fs/promises";
|
|
7
7
|
import { execSync, execFileSync } from "node:child_process";
|
|
8
8
|
import { homedir } from "node:os";
|
|
9
|
-
import type {
|
|
9
|
+
import type {
|
|
10
|
+
BuiltinPolicyDefinition,
|
|
11
|
+
PolicyContext,
|
|
12
|
+
PolicyFunction,
|
|
13
|
+
PolicyResult,
|
|
14
|
+
} from "./policy-types";
|
|
15
|
+
import { POLICY_CATALOG } from "./policy-catalog";
|
|
10
16
|
import { allow, deny, instruct } from "./policy-helpers";
|
|
11
17
|
import { normalizePolicyName, registerPolicy } from "./policy-registry";
|
|
12
18
|
import { hookLogWarn } from "./hook-logger";
|
|
@@ -254,7 +260,7 @@ const SEGMENT_SEPARATORS = /[;&|\n\r(){}`]+/;
|
|
|
254
260
|
* `/usr/bin/env` and `env` behave identically.
|
|
255
261
|
*/
|
|
256
262
|
const COMMAND_PREFIX_TOKENS = new Set([
|
|
257
|
-
"npx", "bunx", "pnpx", "npm", "pnpm", "yarn", "dlx", "exec", "run",
|
|
263
|
+
"npx", "bunx", "pnpx", "npm", "pnpm", "yarn", "dlx", "exec", "run", "eval",
|
|
258
264
|
"node", "bun", "deno", "env", "command", "builtin", "nohup", "setsid",
|
|
259
265
|
"time", "timeout", "nice", "stdbuf", "xargs", "sudo", "doas",
|
|
260
266
|
"sh", "bash", "zsh", "dash", "ksh", "fish", "ash",
|
|
@@ -265,6 +271,15 @@ const COMMAND_PREFIX_TOKENS = new Set([
|
|
|
265
271
|
* `./node_modules/.bin/failproofai`, `.../failproofai/bin/failproofai.mjs`. */
|
|
266
272
|
const SELF_BINARY_TOKEN_RE = /(?:^|\/)failproofai[^/]*$/;
|
|
267
273
|
|
|
274
|
+
/**
|
|
275
|
+
* The same CLI, reached by path rather than by name.
|
|
276
|
+
*
|
|
277
|
+
* `node <pkg>/dist/cli.mjs config --pause` put `node` in command position, so
|
|
278
|
+
* the walk above settled on a path the binary regex did not match — and it
|
|
279
|
+
* paused enforcement. Verified as a live bypass.
|
|
280
|
+
*/
|
|
281
|
+
const SELF_ENTRY_PATH_RE = /failproofai\/(?:dist\/(?:cli|index)\.mjs|bin\/failproofai\.mjs)$/;
|
|
282
|
+
|
|
268
283
|
/** `config`, and the two aliases the entrypoint normalizes to it. */
|
|
269
284
|
const CONFIG_SUBCOMMAND_RE = /^(?:config|configure|setup)$/;
|
|
270
285
|
|
|
@@ -290,7 +305,36 @@ const RUNNER_OPERAND_RE = /^\d+[a-z]*$/i;
|
|
|
290
305
|
* and denies. That is a far narrower miss than matching every mention, and it
|
|
291
306
|
* errs toward refusing rather than toward silently suspending enforcement.
|
|
292
307
|
*/
|
|
293
|
-
|
|
308
|
+
type SelfInvocation = "pause" | "cli";
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Classify a command by what it does to failproofai itself, or null when it
|
|
312
|
+
* does nothing to failproofai at all.
|
|
313
|
+
*
|
|
314
|
+
* `pause` outranks `cli` wherever both appear, because the pause verdict is the
|
|
315
|
+
* one whose message has to explain that suspending enforcement is a human call.
|
|
316
|
+
*/
|
|
317
|
+
function classifySelfInvocation(raw: string): SelfInvocation | null {
|
|
318
|
+
// `${X}` before anything else looks at it: braces are SEGMENT SEPARATORS, so
|
|
319
|
+
// the reference was being split into `$` and `X` and matched nothing, while
|
|
320
|
+
// the shell ran it perfectly well.
|
|
321
|
+
const command = raw.replace(/\$\{([A-Za-z_][A-Za-z0-9_]*)\}/g, "$$$1");
|
|
322
|
+
let found: SelfInvocation | null = null;
|
|
323
|
+
// Variables assigned the binary earlier in the SAME command.
|
|
324
|
+
//
|
|
325
|
+
// `x=failproofai; $x config --pause` put `$x` in command position, matched
|
|
326
|
+
// nothing, and paused enforcement — verified as a live bypass. A shell would
|
|
327
|
+
// have to be re-implemented to resolve this in general; what is cheap and
|
|
328
|
+
// covers the actual trick is remembering what was assigned right here.
|
|
329
|
+
const selfVars = new Set<string>();
|
|
330
|
+
for (const [, name, value] of command.matchAll(/(?:^|[\s;&|])([A-Za-z_][A-Za-z0-9_]*)=("[^"]*"|'[^']*'|[^\s;&|]+)/g)) {
|
|
331
|
+
const bare = value.replace(/^["']|["']$/g, "");
|
|
332
|
+
if (SELF_BINARY_TOKEN_RE.test(bare) || SELF_ENTRY_PATH_RE.test(bare)) selfVars.add(name);
|
|
333
|
+
}
|
|
334
|
+
const isSelfVarRef = (token: string): boolean => {
|
|
335
|
+
const m = /^\$\{?([A-Za-z_][A-Za-z0-9_]*)\}?$/.exec(token);
|
|
336
|
+
return m ? selfVars.has(m[1]) : false;
|
|
337
|
+
};
|
|
294
338
|
for (const segment of command.split(SEGMENT_SEPARATORS)) {
|
|
295
339
|
const tokens = segment.split(/\s+/).filter(Boolean);
|
|
296
340
|
let i = 0;
|
|
@@ -309,16 +353,24 @@ function namesSelfPause(command: string): boolean {
|
|
|
309
353
|
i++;
|
|
310
354
|
}
|
|
311
355
|
if (i >= tokens.length) continue;
|
|
312
|
-
if (
|
|
356
|
+
if (
|
|
357
|
+
!SELF_BINARY_TOKEN_RE.test(tokens[i]) &&
|
|
358
|
+
!SELF_ENTRY_PATH_RE.test(tokens[i]) &&
|
|
359
|
+
!isSelfVarRef(tokens[i])
|
|
360
|
+
) {
|
|
361
|
+
continue;
|
|
362
|
+
}
|
|
313
363
|
|
|
314
|
-
// The binary IS the command here.
|
|
315
|
-
//
|
|
364
|
+
// The binary IS the command here. `config … --pause` is singled out only for
|
|
365
|
+
// its message; every other subcommand is denied just the same.
|
|
316
366
|
const args = tokens.slice(i + 1);
|
|
317
367
|
const configAt = args.findIndex((a) => CONFIG_SUBCOMMAND_RE.test(a));
|
|
318
|
-
if (configAt
|
|
319
|
-
|
|
368
|
+
if (configAt !== -1 && args.slice(configAt + 1).some((a) => PAUSE_FLAG_RE.test(a))) {
|
|
369
|
+
return "pause";
|
|
370
|
+
}
|
|
371
|
+
found = "cli";
|
|
320
372
|
}
|
|
321
|
-
return
|
|
373
|
+
return found;
|
|
322
374
|
}
|
|
323
375
|
|
|
324
376
|
/**
|
|
@@ -400,7 +452,15 @@ const SECRET_FILE_CREDENTIALS_RE = /credentials/;
|
|
|
400
452
|
const GIT_COMMIT_MERGE_RE = /git\s+(commit|merge|rebase|cherry-pick)\b/;
|
|
401
453
|
|
|
402
454
|
// blockFailproofaiCommands
|
|
403
|
-
|
|
455
|
+
/**
|
|
456
|
+
* A command that removes or moves failproofai's own state.
|
|
457
|
+
*
|
|
458
|
+
* Named separately from the binary walk because it never mentions the binary:
|
|
459
|
+
* the target is a path, and the verb is an ordinary file command.
|
|
460
|
+
*/
|
|
461
|
+
const FAILPROOFAI_STATE_WRITE_RE =
|
|
462
|
+
/\b(?:rm|unlink|shred|mv|truncate)\b[^;&|]*\.failproofai(?:\/|\b)/;
|
|
463
|
+
|
|
404
464
|
const FAILPROOFAI_UNINSTALL_RE = /(?:npm\s+(?:uninstall|remove|un|r)\s.*failproofai|bun\s+remove\s.*failproofai|yarn\s+global\s+remove\s+failproofai|pnpm\s+(?:remove|uninstall|un)\s.*failproofai)/;
|
|
405
465
|
|
|
406
466
|
// warnGitAmend
|
|
@@ -968,20 +1028,6 @@ function blockSudo(ctx: PolicyContext): PolicyResult {
|
|
|
968
1028
|
* can run `failproofai audit`. Neither gap should leave pausing reachable, so
|
|
969
1029
|
* this stays narrow, matches the runner forms, and survives that one being off.
|
|
970
1030
|
*/
|
|
971
|
-
function blockSelfPause(ctx: PolicyContext): PolicyResult {
|
|
972
|
-
if (ctx.toolName !== "Bash") return allow();
|
|
973
|
-
const cmd = getCommand(ctx);
|
|
974
|
-
// The raw command AND its shell-unescaped form: a shell strips quotes and
|
|
975
|
-
// backslashes before running the binary, so `fail\proofai config --pause`
|
|
976
|
-
// reaches the pause CLI even though the literal name is broken.
|
|
977
|
-
if (namesSelfPause(cmd) || namesSelfPause(stripShellQuoting(cmd))) {
|
|
978
|
-
return deny(
|
|
979
|
-
"Pausing failproofai enforcement is a human action, not an agent one. " +
|
|
980
|
-
"If a policy is blocking legitimate work, say so and let the operator decide.",
|
|
981
|
-
);
|
|
982
|
-
}
|
|
983
|
-
return allow();
|
|
984
|
-
}
|
|
985
1031
|
|
|
986
1032
|
function blockCurlPipeSh(ctx: PolicyContext): PolicyResult {
|
|
987
1033
|
if (ctx.toolName !== "Bash") return allow();
|
|
@@ -1386,20 +1432,60 @@ function blockWorkOnMain(ctx: PolicyContext): PolicyResult {
|
|
|
1386
1432
|
return allow();
|
|
1387
1433
|
}
|
|
1388
1434
|
|
|
1435
|
+
/**
|
|
1436
|
+
* The one policy that cannot be turned off — see `alwaysOn` on its definition.
|
|
1437
|
+
*
|
|
1438
|
+
* Merged from the former `block-self-pause` and `block-failproofai-commands`,
|
|
1439
|
+
* which were two halves of one guard that disagreed with each other.
|
|
1440
|
+
*
|
|
1441
|
+
* `block-self-pause` had the hardened matcher: it walks off runner prefixes and
|
|
1442
|
+
* re-checks the shell-unescaped form, so `sudo failproofai …`, `npx failproofai
|
|
1443
|
+
* …` and `fail\proofai …` do not get through. But it only ever looked for
|
|
1444
|
+
* `config --pause`. `block-failproofai-commands` had the whole surface — any CLI
|
|
1445
|
+
* call, plus package-manager uninstall — on a regex a single `sudo` defeated.
|
|
1446
|
+
* Keeping the broad surface and dropping the weak matcher is the only
|
|
1447
|
+
* combination stronger than either half.
|
|
1448
|
+
*
|
|
1449
|
+
* They also contradicted each other: `block-self-pause` deliberately ALLOWED
|
|
1450
|
+
* `config --resume`, `config --status` and `policies --install`, while
|
|
1451
|
+
* `block-failproofai-commands` denied them. Both were `defaultEnabled`, so the
|
|
1452
|
+
* deny is what actually happened on every machine and the allow never ran. The
|
|
1453
|
+
* merge keeps the behaviour users have.
|
|
1454
|
+
*/
|
|
1389
1455
|
function blockFailproofaiCommands(ctx: PolicyContext): PolicyResult {
|
|
1390
1456
|
if (ctx.toolName !== "Bash") return allow();
|
|
1391
1457
|
const cmd = getCommand(ctx);
|
|
1458
|
+
// The raw command AND its shell-unescaped form: a shell strips quotes and
|
|
1459
|
+
// backslashes before running the binary, so `fail\proofai config --pause`
|
|
1460
|
+
// reaches the pause CLI even though the literal name is broken.
|
|
1461
|
+
const unescaped = stripShellQuoting(cmd);
|
|
1462
|
+
const kind = classifySelfInvocation(cmd) ?? classifySelfInvocation(unescaped);
|
|
1392
1463
|
|
|
1393
|
-
|
|
1394
|
-
|
|
1464
|
+
if (kind === "pause") {
|
|
1465
|
+
return deny(
|
|
1466
|
+
"Pausing failproofai enforcement is a human action, not an agent one. " +
|
|
1467
|
+
"If a policy is blocking legitimate work, say so and let the operator decide.",
|
|
1468
|
+
);
|
|
1469
|
+
}
|
|
1470
|
+
if (kind === "cli") {
|
|
1395
1471
|
return deny("Running failproofai CLI commands is blocked");
|
|
1396
1472
|
}
|
|
1397
|
-
|
|
1398
|
-
//
|
|
1399
|
-
if (FAILPROOFAI_UNINSTALL_RE.test(cmd)) {
|
|
1473
|
+
// Package-manager removal puts the manager in command position, not the
|
|
1474
|
+
// binary, so the walk above never reaches it.
|
|
1475
|
+
if (FAILPROOFAI_UNINSTALL_RE.test(cmd) || FAILPROOFAI_UNINSTALL_RE.test(unescaped)) {
|
|
1400
1476
|
return deny("Uninstalling failproofai is blocked");
|
|
1401
1477
|
}
|
|
1402
|
-
|
|
1478
|
+
// Deleting the state IS disabling enforcement, without ever naming the binary.
|
|
1479
|
+
// `rm ~/.failproofai/policies/packs/installed.json` switched off every pack
|
|
1480
|
+
// policy on the machine and fail-closed did NOT fire, because a missing store
|
|
1481
|
+
// reads as a fresh machine rather than a broken one — so nothing anywhere
|
|
1482
|
+
// reported it.
|
|
1483
|
+
if (FAILPROOFAI_STATE_WRITE_RE.test(cmd) || FAILPROOFAI_STATE_WRITE_RE.test(unescaped)) {
|
|
1484
|
+
return deny(
|
|
1485
|
+
"Deleting or moving failproofai's own state would switch enforcement off. " +
|
|
1486
|
+
"If a policy is blocking legitimate work, say so and let the operator decide.",
|
|
1487
|
+
);
|
|
1488
|
+
}
|
|
1403
1489
|
return allow();
|
|
1404
1490
|
}
|
|
1405
1491
|
|
|
@@ -2061,561 +2147,113 @@ function requireCiGreenBeforeStop(ctx: PolicyContext): PolicyResult {
|
|
|
2061
2147
|
|
|
2062
2148
|
// -- Registry --
|
|
2063
2149
|
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
} satisfies PolicyParamsSchema,
|
|
2158
|
-
},
|
|
2159
|
-
{
|
|
2160
|
-
name: "block-self-pause",
|
|
2161
|
-
displayTitle: "Tried to pause failproofai enforcement",
|
|
2162
|
-
impact: "An agent that can pause enforcement can switch off every other policy.",
|
|
2163
|
-
description: "Block agents from pausing failproofai enforcement",
|
|
2164
|
-
fn: blockSelfPause,
|
|
2165
|
-
match: { events: ["PreToolUse", "PermissionRequest"], toolNames: ["Bash"] },
|
|
2166
|
-
defaultEnabled: true,
|
|
2167
|
-
category: "Dangerous Commands",
|
|
2168
|
-
},
|
|
2169
|
-
{
|
|
2170
|
-
name: "block-sudo",
|
|
2171
|
-
displayTitle: "Tried to run a command with sudo",
|
|
2172
|
-
impact: "Sudo gives the agent root — blocked unless explicitly allow-listed.",
|
|
2173
|
-
description: "Block sudo commands",
|
|
2174
|
-
fn: blockSudo,
|
|
2175
|
-
// PermissionRequest is Codex's escalation-approval event; fire the same
|
|
2176
|
-
// sudo guard there so Codex sandbox bypasses are blocked too.
|
|
2177
|
-
match: { events: ["PreToolUse", "PermissionRequest"], toolNames: ["Bash"] },
|
|
2178
|
-
defaultEnabled: true,
|
|
2179
|
-
category: "Dangerous Commands",
|
|
2180
|
-
params: {
|
|
2181
|
-
allowPatterns: {
|
|
2182
|
-
type: "string[]",
|
|
2183
|
-
description: "Sudo command patterns to allow, matched token-by-token (e.g. 'sudo systemctl status')",
|
|
2184
|
-
default: [],
|
|
2185
|
-
},
|
|
2186
|
-
} satisfies PolicyParamsSchema,
|
|
2187
|
-
},
|
|
2188
|
-
{
|
|
2189
|
-
name: "block-curl-pipe-sh",
|
|
2190
|
-
displayTitle: "Tried to pipe a downloaded script straight to a shell",
|
|
2191
|
-
impact: "`curl ... | sh` runs unverified remote code on your machine.",
|
|
2192
|
-
description: "Block piping downloads to shell",
|
|
2193
|
-
fn: blockCurlPipeSh,
|
|
2194
|
-
match: { events: ["PreToolUse"], toolNames: ["Bash"] },
|
|
2195
|
-
defaultEnabled: true,
|
|
2196
|
-
category: "Dangerous Commands",
|
|
2197
|
-
},
|
|
2198
|
-
{
|
|
2199
|
-
name: "block-rm-rf",
|
|
2200
|
-
displayTitle: "Tried to recursively delete a system path",
|
|
2201
|
-
impact: "Catches catastrophic `rm -rf /` and Windows equivalents.",
|
|
2202
|
-
description: "Prevent catastrophic deletions",
|
|
2203
|
-
fn: blockRmRf,
|
|
2204
|
-
match: { events: ["PreToolUse"], toolNames: ["Bash"] },
|
|
2205
|
-
defaultEnabled: false,
|
|
2206
|
-
category: "Dangerous Commands",
|
|
2207
|
-
params: {
|
|
2208
|
-
allowPaths: {
|
|
2209
|
-
type: "string[]",
|
|
2210
|
-
description: "Paths that are allowed to be recursively deleted",
|
|
2211
|
-
default: [],
|
|
2212
|
-
},
|
|
2213
|
-
} satisfies PolicyParamsSchema,
|
|
2214
|
-
},
|
|
2215
|
-
{
|
|
2216
|
-
name: "block-failproofai-commands",
|
|
2217
|
-
displayTitle: "Tried to disable or modify failproofai itself",
|
|
2218
|
-
impact: "Prevents the agent from turning off the policies that protect you.",
|
|
2219
|
-
description: "Block failproofai CLI commands and uninstallation",
|
|
2220
|
-
fn: blockFailproofaiCommands,
|
|
2221
|
-
match: { events: ["PreToolUse"], toolNames: ["Bash"] },
|
|
2222
|
-
defaultEnabled: true,
|
|
2223
|
-
category: "Dangerous Commands",
|
|
2224
|
-
},
|
|
2225
|
-
{
|
|
2226
|
-
name: "block-kubectl",
|
|
2227
|
-
displayTitle: "Tried to run a Kubernetes command",
|
|
2228
|
-
impact: "kubectl can change live cluster state — gated unless allow-listed.",
|
|
2229
|
-
description: "Block kubectl commands (Kubernetes cluster mutations)",
|
|
2230
|
-
fn: blockKubectl,
|
|
2231
|
-
match: { events: ["PreToolUse"], toolNames: ["Bash"] },
|
|
2232
|
-
defaultEnabled: false,
|
|
2233
|
-
category: "Infra Commands",
|
|
2234
|
-
params: {
|
|
2235
|
-
allowPatterns: {
|
|
2236
|
-
type: "string[]",
|
|
2237
|
-
description: "kubectl command patterns to allow, matched token-by-token (e.g. 'kubectl get *', 'kubectl describe *')",
|
|
2238
|
-
default: [],
|
|
2239
|
-
},
|
|
2240
|
-
} satisfies PolicyParamsSchema,
|
|
2241
|
-
},
|
|
2242
|
-
{
|
|
2243
|
-
name: "block-terraform",
|
|
2244
|
-
displayTitle: "Tried to run a Terraform/OpenTofu command",
|
|
2245
|
-
impact: "Terraform mutates real infrastructure — gated unless allow-listed.",
|
|
2246
|
-
description: "Block terraform and tofu (OpenTofu) commands",
|
|
2247
|
-
fn: blockTerraform,
|
|
2248
|
-
match: { events: ["PreToolUse"], toolNames: ["Bash"] },
|
|
2249
|
-
defaultEnabled: false,
|
|
2250
|
-
category: "Infra Commands",
|
|
2251
|
-
params: {
|
|
2252
|
-
allowPatterns: {
|
|
2253
|
-
type: "string[]",
|
|
2254
|
-
description: "terraform/tofu command patterns to allow (e.g. 'terraform plan', 'terraform validate')",
|
|
2255
|
-
default: [],
|
|
2256
|
-
},
|
|
2257
|
-
} satisfies PolicyParamsSchema,
|
|
2258
|
-
},
|
|
2259
|
-
{
|
|
2260
|
-
name: "block-aws-cli",
|
|
2261
|
-
displayTitle: "Tried to run an AWS CLI command",
|
|
2262
|
-
impact: "AWS CLI can spend money or break prod — gated.",
|
|
2263
|
-
description: "Block aws CLI commands",
|
|
2264
|
-
fn: blockAwsCli,
|
|
2265
|
-
match: { events: ["PreToolUse"], toolNames: ["Bash"] },
|
|
2266
|
-
defaultEnabled: false,
|
|
2267
|
-
category: "Infra Commands",
|
|
2268
|
-
params: {
|
|
2269
|
-
allowPatterns: {
|
|
2270
|
-
type: "string[]",
|
|
2271
|
-
description: "aws CLI command patterns to allow (e.g. 'aws s3 ls *', 'aws sts get-caller-identity')",
|
|
2272
|
-
default: [],
|
|
2273
|
-
},
|
|
2274
|
-
} satisfies PolicyParamsSchema,
|
|
2275
|
-
},
|
|
2276
|
-
{
|
|
2277
|
-
name: "block-gcloud",
|
|
2278
|
-
displayTitle: "Tried to run a Google Cloud command",
|
|
2279
|
-
impact: "gcloud can spend money or break prod — gated.",
|
|
2280
|
-
description: "Block gcloud (Google Cloud) CLI commands",
|
|
2281
|
-
fn: blockGcloud,
|
|
2282
|
-
match: { events: ["PreToolUse"], toolNames: ["Bash"] },
|
|
2283
|
-
defaultEnabled: false,
|
|
2284
|
-
category: "Infra Commands",
|
|
2285
|
-
params: {
|
|
2286
|
-
allowPatterns: {
|
|
2287
|
-
type: "string[]",
|
|
2288
|
-
description: "gcloud command patterns to allow (e.g. 'gcloud auth list', 'gcloud config list')",
|
|
2289
|
-
default: [],
|
|
2290
|
-
},
|
|
2291
|
-
} satisfies PolicyParamsSchema,
|
|
2292
|
-
},
|
|
2293
|
-
{
|
|
2294
|
-
name: "block-az-cli",
|
|
2295
|
-
displayTitle: "Tried to run an Azure CLI command",
|
|
2296
|
-
impact: "az can spend money or break prod — gated.",
|
|
2297
|
-
description: "Block az (Azure) CLI commands",
|
|
2298
|
-
fn: blockAzCli,
|
|
2299
|
-
match: { events: ["PreToolUse"], toolNames: ["Bash"] },
|
|
2300
|
-
defaultEnabled: false,
|
|
2301
|
-
category: "Infra Commands",
|
|
2302
|
-
params: {
|
|
2303
|
-
allowPatterns: {
|
|
2304
|
-
type: "string[]",
|
|
2305
|
-
description: "az CLI command patterns to allow (e.g. 'az account show', 'az group list')",
|
|
2306
|
-
default: [],
|
|
2307
|
-
},
|
|
2308
|
-
} satisfies PolicyParamsSchema,
|
|
2309
|
-
},
|
|
2310
|
-
{
|
|
2311
|
-
name: "block-helm",
|
|
2312
|
-
displayTitle: "Tried to run a Helm command",
|
|
2313
|
-
impact: "Helm releases mutate cluster state — gated.",
|
|
2314
|
-
description: "Block helm commands",
|
|
2315
|
-
fn: blockHelm,
|
|
2316
|
-
match: { events: ["PreToolUse"], toolNames: ["Bash"] },
|
|
2317
|
-
defaultEnabled: false,
|
|
2318
|
-
category: "Infra Commands",
|
|
2319
|
-
params: {
|
|
2320
|
-
allowPatterns: {
|
|
2321
|
-
type: "string[]",
|
|
2322
|
-
description: "helm command patterns to allow (e.g. 'helm list', 'helm status *')",
|
|
2323
|
-
default: [],
|
|
2324
|
-
},
|
|
2325
|
-
} satisfies PolicyParamsSchema,
|
|
2326
|
-
},
|
|
2327
|
-
{
|
|
2328
|
-
name: "block-gh-pipeline",
|
|
2329
|
-
displayTitle: "Tried to run a privileged GitHub CLI pipeline command",
|
|
2330
|
-
impact: "Catches `gh workflow run`, `gh pr merge`, `gh secret set`, etc.",
|
|
2331
|
-
description: "Block gh CLI pipeline-trigger subcommands (workflow run, run rerun/cancel, pr merge, release create/delete, cache delete, secret set/delete)",
|
|
2332
|
-
fn: blockGhPipeline,
|
|
2333
|
-
match: { events: ["PreToolUse"], toolNames: ["Bash"] },
|
|
2334
|
-
defaultEnabled: false,
|
|
2335
|
-
category: "Infra Commands",
|
|
2336
|
-
params: {
|
|
2337
|
-
allowPatterns: {
|
|
2338
|
-
type: "string[]",
|
|
2339
|
-
description: "gh pipeline command patterns to allow (e.g. specific scripted invocations); read-only gh subcommands like 'gh pr view' and 'gh run list' are not matched by this policy",
|
|
2340
|
-
default: [],
|
|
2341
|
-
},
|
|
2342
|
-
} satisfies PolicyParamsSchema,
|
|
2343
|
-
},
|
|
2344
|
-
{
|
|
2345
|
-
name: "block-secrets-write",
|
|
2346
|
-
displayTitle: "Tried to write a secret-key file",
|
|
2347
|
-
impact: "Stops the agent from creating `.pem`, `id_rsa`, `credentials.json`, etc.",
|
|
2348
|
-
description: "Block writing secret key files",
|
|
2349
|
-
fn: blockSecretsWrite,
|
|
2350
|
-
match: { events: ["PreToolUse"], toolNames: ["Write"] },
|
|
2351
|
-
defaultEnabled: false,
|
|
2352
|
-
category: "Dangerous Commands",
|
|
2353
|
-
params: {
|
|
2354
|
-
additionalPatterns: {
|
|
2355
|
-
type: "string[]",
|
|
2356
|
-
description: "Additional filename patterns (substrings) to block",
|
|
2357
|
-
default: [],
|
|
2358
|
-
},
|
|
2359
|
-
} satisfies PolicyParamsSchema,
|
|
2360
|
-
},
|
|
2361
|
-
{
|
|
2362
|
-
name: "block-push-master",
|
|
2363
|
-
displayTitle: "Tried to push directly to main/master",
|
|
2364
|
-
impact: "Direct pushes to a protected branch bypass review.",
|
|
2365
|
-
description: "Block pushing to main/master",
|
|
2366
|
-
fn: blockPushMaster,
|
|
2367
|
-
match: { events: ["PreToolUse"], toolNames: ["Bash"] },
|
|
2368
|
-
defaultEnabled: true,
|
|
2369
|
-
category: "Git",
|
|
2370
|
-
params: {
|
|
2371
|
-
protectedBranches: {
|
|
2372
|
-
type: "string[]",
|
|
2373
|
-
description: "Branch names to protect from direct pushes",
|
|
2374
|
-
default: ["main", "master"],
|
|
2375
|
-
},
|
|
2376
|
-
} satisfies PolicyParamsSchema,
|
|
2377
|
-
},
|
|
2378
|
-
{
|
|
2379
|
-
name: "block-force-push",
|
|
2380
|
-
displayTitle: "Tried to force-push",
|
|
2381
|
-
impact: "Force-pushes rewrite history and can clobber teammates' work.",
|
|
2382
|
-
description: "Prevent force-pushing to any branch",
|
|
2383
|
-
fn: blockForcePush,
|
|
2384
|
-
match: { events: ["PreToolUse"], toolNames: ["Bash"] },
|
|
2385
|
-
defaultEnabled: false,
|
|
2386
|
-
category: "Git",
|
|
2387
|
-
},
|
|
2388
|
-
{
|
|
2389
|
-
name: "block-work-on-main",
|
|
2390
|
-
displayTitle: "Tried to commit or merge on main/master",
|
|
2391
|
-
impact: "Work should land via PR — direct commits skip review.",
|
|
2392
|
-
description: "Block git commits and merges on main/master branch",
|
|
2393
|
-
fn: blockWorkOnMain,
|
|
2394
|
-
match: { events: ["PreToolUse"], toolNames: ["Bash"] },
|
|
2395
|
-
defaultEnabled: false,
|
|
2396
|
-
category: "Git",
|
|
2397
|
-
params: {
|
|
2398
|
-
protectedBranches: {
|
|
2399
|
-
type: "string[]",
|
|
2400
|
-
description: "Branch names where commits/merges are blocked",
|
|
2401
|
-
default: ["main", "master"],
|
|
2402
|
-
},
|
|
2403
|
-
} satisfies PolicyParamsSchema,
|
|
2404
|
-
},
|
|
2405
|
-
{
|
|
2406
|
-
name: "warn-git-amend",
|
|
2407
|
-
displayTitle: "Used git commit --amend",
|
|
2408
|
-
impact: "Amending after a push rewrites history that others may have pulled.",
|
|
2409
|
-
description: "Warns before amending git commits, which rewrites history",
|
|
2410
|
-
fn: warnGitAmend,
|
|
2411
|
-
match: { events: ["PreToolUse"], toolNames: ["Bash"] },
|
|
2412
|
-
defaultEnabled: false,
|
|
2413
|
-
category: "Git",
|
|
2414
|
-
},
|
|
2415
|
-
{
|
|
2416
|
-
name: "warn-git-stash-drop",
|
|
2417
|
-
displayTitle: "Tried to drop or clear git stash",
|
|
2418
|
-
impact: "Stash deletions are permanent and silent.",
|
|
2419
|
-
description: "Warns before permanently deleting stashed changes",
|
|
2420
|
-
fn: warnGitStashDrop,
|
|
2421
|
-
match: { events: ["PreToolUse"], toolNames: ["Bash"] },
|
|
2422
|
-
defaultEnabled: false,
|
|
2423
|
-
category: "Git",
|
|
2424
|
-
},
|
|
2425
|
-
{
|
|
2426
|
-
name: "warn-all-files-staged",
|
|
2427
|
-
displayTitle: "Staged all files with git add -A / .",
|
|
2428
|
-
impact: "Wide stages routinely catch generated files or secrets you didn't intend to commit.",
|
|
2429
|
-
description: "Warns before staging all working tree files with git add -A / . / --all",
|
|
2430
|
-
fn: warnAllFilesStaged,
|
|
2431
|
-
match: { events: ["PreToolUse"], toolNames: ["Bash"] },
|
|
2432
|
-
defaultEnabled: false,
|
|
2433
|
-
category: "Git",
|
|
2434
|
-
},
|
|
2435
|
-
{
|
|
2436
|
-
name: "warn-destructive-sql",
|
|
2437
|
-
displayTitle: "Ran destructive SQL (DROP / TRUNCATE / DELETE without WHERE)",
|
|
2438
|
-
impact: "Easy way to wipe a table by accident.",
|
|
2439
|
-
description: "Warn before executing destructive SQL (DROP/TRUNCATE/DELETE without WHERE) via database clients",
|
|
2440
|
-
fn: warnDestructiveSql,
|
|
2441
|
-
match: { events: ["PreToolUse"], toolNames: ["Bash"] },
|
|
2442
|
-
defaultEnabled: false,
|
|
2443
|
-
category: "Database",
|
|
2444
|
-
},
|
|
2445
|
-
{
|
|
2446
|
-
name: "warn-schema-alteration",
|
|
2447
|
-
displayTitle: "Altered a database schema column",
|
|
2448
|
-
impact: "ALTER TABLE operations can lock tables and break readers.",
|
|
2449
|
-
description: "Warns before SQL schema changes (ALTER TABLE with column or rename operations)",
|
|
2450
|
-
fn: warnSchemaAlteration,
|
|
2451
|
-
match: { events: ["PreToolUse"], toolNames: ["Bash"] },
|
|
2452
|
-
defaultEnabled: false,
|
|
2453
|
-
category: "Database",
|
|
2454
|
-
},
|
|
2455
|
-
{
|
|
2456
|
-
name: "warn-package-publish",
|
|
2457
|
-
displayTitle: "Tried to publish a package",
|
|
2458
|
-
impact: "Publishes are irreversible — `npm publish` / `cargo publish` shouldn't happen without intent.",
|
|
2459
|
-
description: "Warn before publishing packages to public registries (npm, PyPI, crates.io, RubyGems, etc.)",
|
|
2460
|
-
fn: warnPackagePublish,
|
|
2461
|
-
match: { events: ["PreToolUse"], toolNames: ["Bash"] },
|
|
2462
|
-
defaultEnabled: false,
|
|
2463
|
-
category: "Packages & System",
|
|
2464
|
-
},
|
|
2465
|
-
{
|
|
2466
|
-
name: "warn-global-package-install",
|
|
2467
|
-
displayTitle: "Installed a package globally",
|
|
2468
|
-
impact: "`npm i -g`, `cargo install`, `pip --user` pollute your machine outside the project.",
|
|
2469
|
-
description: "Warns before installing packages globally (npm -g, cargo install, etc.)",
|
|
2470
|
-
fn: warnGlobalPackageInstall,
|
|
2471
|
-
match: { events: ["PreToolUse"], toolNames: ["Bash"] },
|
|
2472
|
-
defaultEnabled: false,
|
|
2473
|
-
category: "Packages & System",
|
|
2474
|
-
},
|
|
2475
|
-
{
|
|
2476
|
-
name: "prefer-package-manager",
|
|
2477
|
-
displayTitle: "Used a non-preferred package manager",
|
|
2478
|
-
impact: "Mixing package managers creates lockfile churn for your team.",
|
|
2479
|
-
description: "Blocks non-preferred package managers and tells Claude to use an allowed one (e.g., uv instead of pip)",
|
|
2480
|
-
fn: preferPackageManager,
|
|
2481
|
-
match: { events: ["PreToolUse"], toolNames: ["Bash"] },
|
|
2482
|
-
defaultEnabled: false,
|
|
2483
|
-
category: "Packages & System",
|
|
2484
|
-
params: {
|
|
2485
|
-
allowed: {
|
|
2486
|
-
type: "string[]",
|
|
2487
|
-
description: "Allowed package manager names (e.g. ['uv', 'bun']). Any detected manager not in this list is blocked.",
|
|
2488
|
-
default: [],
|
|
2489
|
-
},
|
|
2490
|
-
blocked: {
|
|
2491
|
-
type: "string[]",
|
|
2492
|
-
description: "Additional manager names to block beyond the built-in list (e.g. ['pdm', 'pipx']).",
|
|
2493
|
-
default: [],
|
|
2494
|
-
},
|
|
2495
|
-
} satisfies PolicyParamsSchema,
|
|
2496
|
-
},
|
|
2497
|
-
{
|
|
2498
|
-
name: "warn-large-file-write",
|
|
2499
|
-
displayTitle: "Wrote a file larger than the configured threshold",
|
|
2500
|
-
impact: "Catches accidentally large file writes (logs, binaries, model dumps).",
|
|
2501
|
-
description: "Warn before writing files larger than 1MB (configurable via thresholdKb param)",
|
|
2502
|
-
fn: warnLargeFileWrite,
|
|
2503
|
-
match: { events: ["PreToolUse"], toolNames: ["Write"] },
|
|
2504
|
-
defaultEnabled: false,
|
|
2505
|
-
category: "Packages & System",
|
|
2506
|
-
params: {
|
|
2507
|
-
thresholdKb: {
|
|
2508
|
-
type: "number",
|
|
2509
|
-
description: "File size threshold in KB above which a warning is issued",
|
|
2510
|
-
default: 1024,
|
|
2511
|
-
},
|
|
2512
|
-
} satisfies PolicyParamsSchema,
|
|
2513
|
-
},
|
|
2514
|
-
{
|
|
2515
|
-
name: "warn-background-process",
|
|
2516
|
-
displayTitle: "Started a long-lived background process",
|
|
2517
|
-
impact: "Catches `nohup` / `&` / `screen` / `tmux` / `disown` patterns that the agent often forgets to clean up.",
|
|
2518
|
-
description: "Warns before starting detached or background processes",
|
|
2519
|
-
fn: warnBackgroundProcess,
|
|
2520
|
-
match: { events: ["PreToolUse"], toolNames: ["Bash"] },
|
|
2521
|
-
defaultEnabled: false,
|
|
2522
|
-
category: "Packages & System",
|
|
2523
|
-
},
|
|
2524
|
-
{
|
|
2525
|
-
name: "warn-repeated-tool-calls",
|
|
2526
|
-
displayTitle: "Called the same tool 3+ times with identical arguments",
|
|
2527
|
-
impact: "Usually a sign of a stuck loop burning tokens.",
|
|
2528
|
-
description: "Warn when the same tool is called 3+ times with identical parameters",
|
|
2529
|
-
fn: warnRepeatedToolCalls,
|
|
2530
|
-
match: { events: ["PreToolUse"] },
|
|
2531
|
-
defaultEnabled: false,
|
|
2532
|
-
category: "AI Behavior",
|
|
2533
|
-
},
|
|
2534
|
-
{
|
|
2535
|
-
name: "require-commit-before-stop",
|
|
2536
|
-
displayTitle: "Stopped with uncommitted changes",
|
|
2537
|
-
impact: "Work not in a commit is invisible to teammates and easy to lose.",
|
|
2538
|
-
description: "Require all changes to be committed before Claude stops",
|
|
2539
|
-
fn: requireCommitBeforeStop,
|
|
2540
|
-
match: { events: ["Stop"] },
|
|
2541
|
-
defaultEnabled: false,
|
|
2542
|
-
category: "Workflow",
|
|
2543
|
-
},
|
|
2544
|
-
{
|
|
2545
|
-
name: "require-push-before-stop",
|
|
2546
|
-
displayTitle: "Stopped with unpushed commits",
|
|
2547
|
-
impact: "Local-only commits won't trigger CI or be reviewable.",
|
|
2548
|
-
description: "Require all commits to be pushed to remote before Claude stops",
|
|
2549
|
-
fn: requirePushBeforeStop,
|
|
2550
|
-
match: { events: ["Stop"] },
|
|
2551
|
-
defaultEnabled: false,
|
|
2552
|
-
category: "Workflow",
|
|
2553
|
-
params: {
|
|
2554
|
-
remote: {
|
|
2555
|
-
type: "string",
|
|
2556
|
-
description: "Remote name to push to (default: origin)",
|
|
2557
|
-
default: "origin",
|
|
2558
|
-
},
|
|
2559
|
-
baseBranch: {
|
|
2560
|
-
type: "string",
|
|
2561
|
-
description: "Base branch to compare against (default: main)",
|
|
2562
|
-
default: "main",
|
|
2563
|
-
},
|
|
2564
|
-
} satisfies PolicyParamsSchema,
|
|
2565
|
-
},
|
|
2566
|
-
{
|
|
2567
|
-
name: "require-pr-before-stop",
|
|
2568
|
-
displayTitle: "Stopped without a PR for the branch",
|
|
2569
|
-
impact: "Branches without PRs don't get reviewed.",
|
|
2570
|
-
description: "Require a pull request to exist for the current branch before Claude stops",
|
|
2571
|
-
fn: requirePrBeforeStop,
|
|
2572
|
-
match: { events: ["Stop"] },
|
|
2573
|
-
defaultEnabled: false,
|
|
2574
|
-
category: "Workflow",
|
|
2575
|
-
params: {
|
|
2576
|
-
baseBranch: {
|
|
2577
|
-
type: "string",
|
|
2578
|
-
description: "Base branch to compare against (default: main)",
|
|
2579
|
-
default: "main",
|
|
2580
|
-
},
|
|
2581
|
-
} satisfies PolicyParamsSchema,
|
|
2582
|
-
},
|
|
2583
|
-
{
|
|
2584
|
-
name: "require-no-conflicts-before-stop",
|
|
2585
|
-
displayTitle: "Stopped with a branch that conflicts with main",
|
|
2586
|
-
impact: "Conflicting branches can't merge — surface them early.",
|
|
2587
|
-
description: "Require the current branch to merge cleanly with the base branch before Claude stops",
|
|
2588
|
-
fn: requireNoConflictsBeforeStop,
|
|
2589
|
-
match: { events: ["Stop"] },
|
|
2590
|
-
defaultEnabled: false,
|
|
2591
|
-
category: "Workflow",
|
|
2592
|
-
params: {
|
|
2593
|
-
baseBranch: {
|
|
2594
|
-
type: "string",
|
|
2595
|
-
description: "Base branch to check for conflicts against (default: main)",
|
|
2596
|
-
default: "main",
|
|
2597
|
-
},
|
|
2598
|
-
} satisfies PolicyParamsSchema,
|
|
2599
|
-
},
|
|
2600
|
-
{
|
|
2601
|
-
name: "require-ci-green-before-stop",
|
|
2602
|
-
displayTitle: "Stopped with failing CI",
|
|
2603
|
-
impact: "Failing CI blocks deploy.",
|
|
2604
|
-
description: "Require CI checks to pass on the current HEAD commit before Claude stops (ignores stale runs on prior commits)",
|
|
2605
|
-
fn: requireCiGreenBeforeStop,
|
|
2606
|
-
match: { events: ["Stop"] },
|
|
2607
|
-
defaultEnabled: false,
|
|
2608
|
-
category: "Workflow",
|
|
2609
|
-
},
|
|
2610
|
-
];
|
|
2150
|
+
/**
|
|
2151
|
+
* Name → implementation. The other half of {@link POLICY_CATALOG}.
|
|
2152
|
+
*
|
|
2153
|
+
* Each value is the identical hoisted function object, never a wrapper. Two
|
|
2154
|
+
* things depend on that and neither fails loudly: `audit/cache.ts` hashes
|
|
2155
|
+
* `fn.toString()` into the audit cache's `engineVersion`, so wrapping every
|
|
2156
|
+
* entry would collapse 39 distinct hashes into one and freeze the key — stale
|
|
2157
|
+
* audit results would then be served for the full 30-day TTL with no symptom;
|
|
2158
|
+
* and `gitBranchCache` is module-scoped, so a per-call factory would silently
|
|
2159
|
+
* reset it on every hook event.
|
|
2160
|
+
*/
|
|
2161
|
+
const POLICY_IMPLEMENTATIONS: Record<string, PolicyFunction> = {
|
|
2162
|
+
"sanitize-jwt": sanitizeJwt,
|
|
2163
|
+
"sanitize-api-keys": sanitizeApiKeys,
|
|
2164
|
+
"sanitize-connection-strings": sanitizeConnectionStrings,
|
|
2165
|
+
"sanitize-private-key-content": sanitizePrivateKeyContent,
|
|
2166
|
+
"sanitize-bearer-tokens": sanitizeBearerTokens,
|
|
2167
|
+
"protect-env-vars": protectEnvVars,
|
|
2168
|
+
"block-env-files": blockEnvFiles,
|
|
2169
|
+
"block-read-outside-cwd": blockReadOutsideCwd,
|
|
2170
|
+
"block-sudo": blockSudo,
|
|
2171
|
+
"block-curl-pipe-sh": blockCurlPipeSh,
|
|
2172
|
+
"block-rm-rf": blockRmRf,
|
|
2173
|
+
"block-failproofai-commands": blockFailproofaiCommands,
|
|
2174
|
+
"block-kubectl": blockKubectl,
|
|
2175
|
+
"block-terraform": blockTerraform,
|
|
2176
|
+
"block-aws-cli": blockAwsCli,
|
|
2177
|
+
"block-gcloud": blockGcloud,
|
|
2178
|
+
"block-az-cli": blockAzCli,
|
|
2179
|
+
"block-helm": blockHelm,
|
|
2180
|
+
"block-gh-pipeline": blockGhPipeline,
|
|
2181
|
+
"block-secrets-write": blockSecretsWrite,
|
|
2182
|
+
"block-push-master": blockPushMaster,
|
|
2183
|
+
"block-force-push": blockForcePush,
|
|
2184
|
+
"block-work-on-main": blockWorkOnMain,
|
|
2185
|
+
"warn-git-amend": warnGitAmend,
|
|
2186
|
+
"warn-git-stash-drop": warnGitStashDrop,
|
|
2187
|
+
"warn-all-files-staged": warnAllFilesStaged,
|
|
2188
|
+
"warn-destructive-sql": warnDestructiveSql,
|
|
2189
|
+
"warn-schema-alteration": warnSchemaAlteration,
|
|
2190
|
+
"warn-package-publish": warnPackagePublish,
|
|
2191
|
+
"warn-global-package-install": warnGlobalPackageInstall,
|
|
2192
|
+
"prefer-package-manager": preferPackageManager,
|
|
2193
|
+
"warn-large-file-write": warnLargeFileWrite,
|
|
2194
|
+
"warn-background-process": warnBackgroundProcess,
|
|
2195
|
+
"warn-repeated-tool-calls": warnRepeatedToolCalls,
|
|
2196
|
+
"require-commit-before-stop": requireCommitBeforeStop,
|
|
2197
|
+
"require-push-before-stop": requirePushBeforeStop,
|
|
2198
|
+
"require-pr-before-stop": requirePrBeforeStop,
|
|
2199
|
+
"require-no-conflicts-before-stop": requireNoConflictsBeforeStop,
|
|
2200
|
+
"require-ci-green-before-stop": requireCiGreenBeforeStop,
|
|
2201
|
+
};
|
|
2202
|
+
|
|
2203
|
+
/**
|
|
2204
|
+
* Catalog and implementations must be in BIJECTION, and this throws rather than
|
|
2205
|
+
* warns because the failure is otherwise invisible in the worst direction: a
|
|
2206
|
+
* name present here but not there yields `fn: undefined`, `registerPolicy`
|
|
2207
|
+
* stores it unvalidated, and the `TypeError` at `await policy.fn(ctx)` is
|
|
2208
|
+
* swallowed by `policy-evaluator.ts` (warn, count, `continue`). The hook then
|
|
2209
|
+
* ALLOWS, exits 0, and still lists the policy in `matchedPolicies` — a machine
|
|
2210
|
+
* reporting that a guard ran when it never did.
|
|
2211
|
+
*/
|
|
2212
|
+
function assertCatalogBijection(): void {
|
|
2213
|
+
const implNames = new Set(Object.keys(POLICY_IMPLEMENTATIONS));
|
|
2214
|
+
const missing = POLICY_CATALOG.filter((e) => !implNames.has(e.name)).map((e) => e.name);
|
|
2215
|
+
if (missing.length > 0) {
|
|
2216
|
+
throw new Error(
|
|
2217
|
+
`failproofai: builtin policies missing an implementation: ${missing.join(", ")}`,
|
|
2218
|
+
);
|
|
2219
|
+
}
|
|
2220
|
+
const catalogNames = new Set(POLICY_CATALOG.map((e) => e.name));
|
|
2221
|
+
const orphaned = [...implNames].filter((n) => !catalogNames.has(n));
|
|
2222
|
+
if (orphaned.length > 0) {
|
|
2223
|
+
throw new Error(
|
|
2224
|
+
`failproofai: policy implementations with no catalog entry: ${orphaned.join(", ")}`,
|
|
2225
|
+
);
|
|
2226
|
+
}
|
|
2227
|
+
}
|
|
2228
|
+
assertCatalogBijection();
|
|
2229
|
+
|
|
2230
|
+
/**
|
|
2231
|
+
* The joined view, and the shape every consumer has always seen.
|
|
2232
|
+
*
|
|
2233
|
+
* Deliberately a positional, spread-only, eager `.map()`: it preserves catalog
|
|
2234
|
+
* order, adds no fields, fills no defaults, drops no rows and calls no factory.
|
|
2235
|
+
* Each of those alternatives changes observable behaviour — see the rules on
|
|
2236
|
+
* {@link POLICY_CATALOG} and the invariants in
|
|
2237
|
+
* `__tests__/hooks/policy-catalog.test.ts`.
|
|
2238
|
+
*/
|
|
2239
|
+
export const BUILTIN_POLICIES: BuiltinPolicyDefinition[] = POLICY_CATALOG.map((entry) => ({
|
|
2240
|
+
...entry,
|
|
2241
|
+
fn: POLICY_IMPLEMENTATIONS[entry.name] as PolicyFunction,
|
|
2242
|
+
}));
|
|
2611
2243
|
|
|
2612
2244
|
export function registerBuiltinPolicies(enabledNames: string[]): void {
|
|
2613
2245
|
// Tolerate both flat ("sanitize-jwt") and qualified ("failproofai/sanitize-jwt")
|
|
2614
2246
|
// forms in the user's enabledPolicies config — canonicalize both sides.
|
|
2615
2247
|
const enabledSet = new Set(enabledNames.map(normalizePolicyName));
|
|
2616
2248
|
for (const policy of BUILTIN_POLICIES) {
|
|
2617
|
-
|
|
2618
|
-
|
|
2249
|
+
// `alwaysOn` deliberately bypasses the enabled set, and the caller's three
|
|
2250
|
+
// ways of producing an empty one with it: a policy the user never enabled,
|
|
2251
|
+
// an active session pause (`handler.ts` passes `[]`), and a config file that
|
|
2252
|
+
// failed to parse (`hooks-config.ts` soft-fails to `{enabledPolicies: []}`).
|
|
2253
|
+
// A guard against the agent disabling failproofai that any of those can
|
|
2254
|
+
// switch off is not a guard.
|
|
2255
|
+
if (policy.alwaysOn || enabledSet.has(normalizePolicyName(policy.name))) {
|
|
2256
|
+
registerPolicy(policy.name, policy.description, policy.fn, policy.match, 0, policy.params);
|
|
2619
2257
|
}
|
|
2620
2258
|
}
|
|
2621
2259
|
}
|