failproofai 0.0.6 → 0.0.7
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 +3 -3
- 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.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 +2 -2
- package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +7 -7
- package/.next/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +3 -3
- 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.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 +2 -2
- 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/_head.segment.rsc +4 -4
- package/.next/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +10 -10
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +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 +2 -2
- package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +15 -15
- package/.next/standalone/.next/server/app/index.segments/_head.segment.rsc +4 -4
- package/.next/standalone/.next/server/app/index.segments/_index.segment.rsc +10 -10
- package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/page/server-reference-manifest.json +1 -1
- 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 +8 -8
- 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.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.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.nft.json +1 -1
- package/.next/standalone/.next/server/app/projects/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0g72weg._.js +1 -1
- package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_0z7w.hh._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__092s1ta._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__09icjsf._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0g.lg8b._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0h..k-e._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0okos0k._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0t3ka1q._.js → [root-of-the-server]__0tjjyb9._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0w6l33k._.js +7 -7
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0ow37ro._.js → [root-of-the-server]__0zn7uo6._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__11pa2ra._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__12t-wym._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/_10lm7or._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/app_global-error_tsx_0xerkr6._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_policies_hooks-client_tsx_0q-m0y-._.js +1 -1
- package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
- package/.next/standalone/.next/server/pages/404.html +2 -2
- 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 +9 -9
- package/.next/standalone/.next/static/chunks/{0lq8ary5l4s8t.js → 01l2mh88iy.ga.js} +1 -1
- package/.next/standalone/.next/static/chunks/{04iuhj_-h-21-.js → 0388wpenm9-a4.js} +1 -1
- package/.next/standalone/.next/static/chunks/{0ubv3x~0zdd_w.js → 0a0lh_a4f_xs-.js} +1 -1
- package/.next/standalone/.next/static/chunks/{13cot7j99xkb~.js → 0f_9854du76y2.js} +1 -1
- package/.next/standalone/.next/static/chunks/{0-igg2k65fzo_.js → 0j2o20pqkib~d.js} +1 -1
- package/.next/standalone/.next/static/chunks/{0k5t-n0s8p2nr.js → 0kkzzoo.s-t3p.js} +1 -1
- package/.next/standalone/.next/static/chunks/{0v23ca5xty1n~.js → 0x0o8~u4jsatb.js} +2 -2
- package/.next/standalone/.next/static/chunks/{061hxr2b-j.6q.js → 12wu.28cbx4dl.js} +1 -1
- package/.next/standalone/package.json +1 -1
- package/.next/standalone/server.js +1 -1
- package/dist/cli.mjs +203 -46
- package/package.json +1 -1
- package/src/hooks/builtin-policies.ts +196 -44
- package/src/hooks/custom-hooks-loader.ts +6 -3
- package/src/hooks/hooks-config.ts +31 -5
- /package/.next/standalone/.next/static/{my01WPjry7ohRUHyTaYp4 → 9FNjQiktocMN-qDiGqDL5}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{my01WPjry7ohRUHyTaYp4 → 9FNjQiktocMN-qDiGqDL5}/_clientMiddlewareManifest.js +0 -0
- /package/.next/standalone/.next/static/{my01WPjry7ohRUHyTaYp4 → 9FNjQiktocMN-qDiGqDL5}/_ssgManifest.js +0 -0
package/dist/cli.mjs
CHANGED
|
@@ -103,7 +103,7 @@ var init_hook_logger = __esm(() => {
|
|
|
103
103
|
});
|
|
104
104
|
|
|
105
105
|
// src/hooks/hooks-config.ts
|
|
106
|
-
import { readFileSync, writeFileSync, existsSync as existsSync2, mkdirSync as mkdirSync2 } from "node:fs";
|
|
106
|
+
import { readFileSync, writeFileSync, existsSync as existsSync2, mkdirSync as mkdirSync2, statSync as statSync2 } from "node:fs";
|
|
107
107
|
import { resolve, dirname } from "node:path";
|
|
108
108
|
import { homedir as homedir2 } from "node:os";
|
|
109
109
|
function readConfigAt(path) {
|
|
@@ -117,8 +117,24 @@ function readConfigAt(path) {
|
|
|
117
117
|
return {};
|
|
118
118
|
}
|
|
119
119
|
}
|
|
120
|
+
function findProjectConfigDir(start) {
|
|
121
|
+
const home = homedir2();
|
|
122
|
+
let dir = resolve(start);
|
|
123
|
+
while (dir !== home) {
|
|
124
|
+
const marker = resolve(dir, ".failproofai");
|
|
125
|
+
try {
|
|
126
|
+
if (statSync2(marker).isDirectory())
|
|
127
|
+
return dir;
|
|
128
|
+
} catch {}
|
|
129
|
+
const parent = dirname(dir);
|
|
130
|
+
if (parent === dir)
|
|
131
|
+
break;
|
|
132
|
+
dir = parent;
|
|
133
|
+
}
|
|
134
|
+
return resolve(start);
|
|
135
|
+
}
|
|
120
136
|
function readMergedHooksConfig(cwd) {
|
|
121
|
-
const base =
|
|
137
|
+
const base = findProjectConfigDir(cwd ?? process.cwd());
|
|
122
138
|
const projectPath = resolve(base, ".failproofai", "policies-config.json");
|
|
123
139
|
const localPath = resolve(base, ".failproofai", "policies-config.local.json");
|
|
124
140
|
const globalPath = resolve(homedir2(), ".failproofai", "policies-config.json");
|
|
@@ -150,14 +166,13 @@ function readMergedHooksConfig(cwd) {
|
|
|
150
166
|
};
|
|
151
167
|
}
|
|
152
168
|
function getConfigPathForScope(scope, cwd) {
|
|
153
|
-
const base = cwd ? resolve(cwd) : process.cwd();
|
|
154
169
|
switch (scope) {
|
|
155
170
|
case "user":
|
|
156
171
|
return resolve(homedir2(), ".failproofai", "policies-config.json");
|
|
157
172
|
case "project":
|
|
158
|
-
return resolve(
|
|
173
|
+
return resolve(findProjectConfigDir(cwd ?? process.cwd()), ".failproofai", "policies-config.json");
|
|
159
174
|
case "local":
|
|
160
|
-
return resolve(
|
|
175
|
+
return resolve(findProjectConfigDir(cwd ?? process.cwd()), ".failproofai", "policies-config.local.json");
|
|
161
176
|
}
|
|
162
177
|
}
|
|
163
178
|
function readScopedHooksConfig(scope, cwd) {
|
|
@@ -768,6 +783,38 @@ function blockFailproofaiCommands(ctx) {
|
|
|
768
783
|
}
|
|
769
784
|
return allow();
|
|
770
785
|
}
|
|
786
|
+
function blockInfraCli(ctx, re, denyMsg) {
|
|
787
|
+
if (ctx.toolName !== "Bash")
|
|
788
|
+
return allow();
|
|
789
|
+
const cmd = getCommand(ctx);
|
|
790
|
+
if (!re.test(cmd))
|
|
791
|
+
return allow();
|
|
792
|
+
const allowPatterns = ctx.params?.allowPatterns ?? [];
|
|
793
|
+
if (allowPatterns.some((p) => matchesAllowedPattern(cmd, p)))
|
|
794
|
+
return allow();
|
|
795
|
+
return deny(denyMsg);
|
|
796
|
+
}
|
|
797
|
+
function blockKubectl(ctx) {
|
|
798
|
+
return blockInfraCli(ctx, KUBECTL_RE, "kubectl commands are blocked");
|
|
799
|
+
}
|
|
800
|
+
function blockTerraform(ctx) {
|
|
801
|
+
return blockInfraCli(ctx, TERRAFORM_RE, "terraform/tofu commands are blocked");
|
|
802
|
+
}
|
|
803
|
+
function blockAwsCli(ctx) {
|
|
804
|
+
return blockInfraCli(ctx, AWS_CLI_RE, "aws CLI commands are blocked");
|
|
805
|
+
}
|
|
806
|
+
function blockGcloud(ctx) {
|
|
807
|
+
return blockInfraCli(ctx, GCLOUD_RE, "gcloud commands are blocked");
|
|
808
|
+
}
|
|
809
|
+
function blockAzCli(ctx) {
|
|
810
|
+
return blockInfraCli(ctx, AZ_CLI_RE, "az (Azure) CLI commands are blocked");
|
|
811
|
+
}
|
|
812
|
+
function blockHelm(ctx) {
|
|
813
|
+
return blockInfraCli(ctx, HELM_RE, "helm commands are blocked");
|
|
814
|
+
}
|
|
815
|
+
function blockGhPipeline(ctx) {
|
|
816
|
+
return blockInfraCli(ctx, GH_PIPELINE_RE, "gh pipeline-trigger commands are blocked");
|
|
817
|
+
}
|
|
771
818
|
async function warnRepeatedToolCalls(ctx) {
|
|
772
819
|
const THRESHOLD = 3;
|
|
773
820
|
const transcriptPath = ctx.session?.transcriptPath;
|
|
@@ -1060,7 +1107,31 @@ function requireNoConflictsBeforeStop(ctx) {
|
|
|
1060
1107
|
if (branch === baseBranch) {
|
|
1061
1108
|
return allow(`On base branch "${baseBranch}", skipping conflict check.`);
|
|
1062
1109
|
}
|
|
1063
|
-
|
|
1110
|
+
try {
|
|
1111
|
+
execSync("gh --version", { cwd, encoding: "utf8", stdio: ["pipe", "pipe", "pipe"], timeout: 3000 });
|
|
1112
|
+
} catch {
|
|
1113
|
+
return allow("gh CLI not installed, skipping conflict check.");
|
|
1114
|
+
}
|
|
1115
|
+
let prJson;
|
|
1116
|
+
try {
|
|
1117
|
+
prJson = execSync("gh pr view --json mergeable,number,url,state", {
|
|
1118
|
+
cwd,
|
|
1119
|
+
encoding: "utf8",
|
|
1120
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
1121
|
+
timeout: 15000
|
|
1122
|
+
}).trim();
|
|
1123
|
+
} catch {
|
|
1124
|
+
return allow("No pull request found for branch, skipping conflict check.");
|
|
1125
|
+
}
|
|
1126
|
+
let pr;
|
|
1127
|
+
try {
|
|
1128
|
+
pr = JSON.parse(prJson);
|
|
1129
|
+
} catch {
|
|
1130
|
+
return allow("Could not parse gh pr view output, skipping conflict check.");
|
|
1131
|
+
}
|
|
1132
|
+
if (pr.state !== "OPEN") {
|
|
1133
|
+
return allow(`PR #${pr.number} is ${pr.state.toLowerCase()}; skipping conflict check.`);
|
|
1134
|
+
}
|
|
1064
1135
|
try {
|
|
1065
1136
|
execFileSync("git", ["rev-parse", "--verify", `origin/${baseBranch}`], {
|
|
1066
1137
|
cwd,
|
|
@@ -1069,9 +1140,7 @@ function requireNoConflictsBeforeStop(ctx) {
|
|
|
1069
1140
|
timeout: 3000
|
|
1070
1141
|
});
|
|
1071
1142
|
const ahead = execFileSync("git", ["log", `origin/${baseBranch}..HEAD`, "--oneline"], { cwd, encoding: "utf8", stdio: ["pipe", "pipe", "pipe"], timeout: 5000 }).trim();
|
|
1072
|
-
if (
|
|
1073
|
-
localSkipped = true;
|
|
1074
|
-
} else {
|
|
1143
|
+
if (ahead) {
|
|
1075
1144
|
execFileSync("git", ["merge-tree", "--write-tree", "--name-only", `origin/${baseBranch}`, "HEAD"], { cwd, encoding: "utf8", stdio: ["pipe", "pipe", "pipe"], timeout: 1e4 });
|
|
1076
1145
|
}
|
|
1077
1146
|
} catch (err) {
|
|
@@ -1090,32 +1159,6 @@ function requireNoConflictsBeforeStop(ctx) {
|
|
|
1090
1159
|
const fileList = files.length ? files.join(", ") : "one or more files";
|
|
1091
1160
|
return deny(`Branch "${branch}" has merge conflicts with ${baseBranch} in: ${fileList}. ` + `Rebase or merge origin/${baseBranch} now and resolve the conflicts.`);
|
|
1092
1161
|
}
|
|
1093
|
-
localSkipped = true;
|
|
1094
|
-
}
|
|
1095
|
-
try {
|
|
1096
|
-
execSync("gh --version", { cwd, encoding: "utf8", stdio: ["pipe", "pipe", "pipe"], timeout: 3000 });
|
|
1097
|
-
} catch {
|
|
1098
|
-
return allow(localSkipped ? "Local conflict check skipped and gh CLI not installed, skipping conflict check." : `Branch "${branch}" merges cleanly with ${baseBranch} locally (gh CLI not installed, PR mergeability not verified).`);
|
|
1099
|
-
}
|
|
1100
|
-
let prJson;
|
|
1101
|
-
try {
|
|
1102
|
-
prJson = execSync("gh pr view --json mergeable,number,url,state", {
|
|
1103
|
-
cwd,
|
|
1104
|
-
encoding: "utf8",
|
|
1105
|
-
stdio: ["pipe", "pipe", "pipe"],
|
|
1106
|
-
timeout: 15000
|
|
1107
|
-
}).trim();
|
|
1108
|
-
} catch {
|
|
1109
|
-
return allow(localSkipped ? "No pull request found for branch, skipping conflict check." : `Branch "${branch}" merges cleanly with ${baseBranch} locally (no PR to verify against).`);
|
|
1110
|
-
}
|
|
1111
|
-
let pr;
|
|
1112
|
-
try {
|
|
1113
|
-
pr = JSON.parse(prJson);
|
|
1114
|
-
} catch {
|
|
1115
|
-
return allow("Could not parse gh pr view output, skipping PR mergeability check.");
|
|
1116
|
-
}
|
|
1117
|
-
if (pr.state !== "OPEN") {
|
|
1118
|
-
return allow(`PR #${pr.number} is ${pr.state.toLowerCase()}; skipping conflict check.`);
|
|
1119
1162
|
}
|
|
1120
1163
|
if (pr.mergeable === "CONFLICTING") {
|
|
1121
1164
|
return deny(`PR #${pr.number} has merge conflicts per GitHub (${pr.url}). ` + `Rebase or merge origin/${baseBranch} now and resolve the conflicts.`);
|
|
@@ -1178,7 +1221,7 @@ function registerBuiltinPolicies(enabledNames) {
|
|
|
1178
1221
|
}
|
|
1179
1222
|
}
|
|
1180
1223
|
}
|
|
1181
|
-
var SHELL_OPERATORS, SHELL_METACHAR_RE, JWT_RE, API_KEY_PATTERNS, CONNECTION_STRING_RE, PRIVATE_KEY_RE, BEARER_TOKEN_RE, SQL_TOOL_RE, DESTRUCTIVE_SQL_RE, DELETE_NO_WHERE_RE, SQL_WHERE_RE, SCHEMA_ALTER_RE, PUBLISH_CMD_RE, ENV_PRINTENV_RE, ECHO_ENV_RE, EXPORT_RE, PS_ENV_VAR_RE, PS_CHILDITEM_ENV_RE, DOTNET_GETENV_RE, CMD_ECHO_ENV_RE, ENV_FILE_PATH_RE, ENV_CMD_RE, SUDO_RE, PS_ELEVATION_RE, RUNAS_RE, CURL_PIPE_SH_RE, PS_WEB_PIPE_RE, FORCE_PUSH_RE, SECRET_FILE_RE, SECRET_FILE_ID_RSA_RE, SECRET_FILE_CREDENTIALS_RE, GIT_COMMIT_MERGE_RE, FAILPROOFAI_CLI_RE, FAILPROOFAI_UNINSTALL_RE, GIT_AMEND_RE, GIT_STASH_DROP_RE, GIT_ADD_ALL_RE, NPM_GLOBAL_RE, YARN_GLOBAL_RE, PNPM_GLOBAL_RE, BUN_GLOBAL_RE, CARGO_INSTALL_RE, PIP_SYSTEM_RE, PKG_MANAGER_DETECTORS, NOHUP_RE, SCREEN_DETACH_RE, TMUX_DETACH_RE, DISOWN_RE, BACKGROUND_AMPERSAND_RE, gitBranchCache, READ_LIKE_CMDS, TOOL_CALL_TRACKER_MAX_BYTES = 65536, SEGMENT_SPLIT_RE, BUILTIN_POLICIES;
|
|
1224
|
+
var SHELL_OPERATORS, SHELL_METACHAR_RE, JWT_RE, API_KEY_PATTERNS, CONNECTION_STRING_RE, PRIVATE_KEY_RE, BEARER_TOKEN_RE, SQL_TOOL_RE, DESTRUCTIVE_SQL_RE, DELETE_NO_WHERE_RE, SQL_WHERE_RE, SCHEMA_ALTER_RE, PUBLISH_CMD_RE, ENV_PRINTENV_RE, ECHO_ENV_RE, EXPORT_RE, PS_ENV_VAR_RE, PS_CHILDITEM_ENV_RE, DOTNET_GETENV_RE, CMD_ECHO_ENV_RE, ENV_FILE_PATH_RE, ENV_CMD_RE, SUDO_RE, PS_ELEVATION_RE, RUNAS_RE, CURL_PIPE_SH_RE, PS_WEB_PIPE_RE, FORCE_PUSH_RE, SECRET_FILE_RE, SECRET_FILE_ID_RSA_RE, SECRET_FILE_CREDENTIALS_RE, GIT_COMMIT_MERGE_RE, FAILPROOFAI_CLI_RE, FAILPROOFAI_UNINSTALL_RE, GIT_AMEND_RE, GIT_STASH_DROP_RE, GIT_ADD_ALL_RE, NPM_GLOBAL_RE, YARN_GLOBAL_RE, PNPM_GLOBAL_RE, BUN_GLOBAL_RE, CARGO_INSTALL_RE, PIP_SYSTEM_RE, PKG_MANAGER_DETECTORS, NOHUP_RE, SCREEN_DETACH_RE, TMUX_DETACH_RE, DISOWN_RE, BACKGROUND_AMPERSAND_RE, KUBECTL_RE, TERRAFORM_RE, AWS_CLI_RE, GCLOUD_RE, AZ_CLI_RE, HELM_RE, GH_PIPELINE_RE, gitBranchCache, READ_LIKE_CMDS, TOOL_CALL_TRACKER_MAX_BYTES = 65536, SEGMENT_SPLIT_RE, BUILTIN_POLICIES;
|
|
1182
1225
|
var init_builtin_policies = __esm(() => {
|
|
1183
1226
|
init_hook_logger();
|
|
1184
1227
|
SHELL_OPERATORS = new Set(["&&", "||", "|", ";"]);
|
|
@@ -1251,6 +1294,13 @@ var init_builtin_policies = __esm(() => {
|
|
|
1251
1294
|
TMUX_DETACH_RE = /\btmux\s+(?:new-session|new)\b[^|&;]*-d\b/;
|
|
1252
1295
|
DISOWN_RE = /\bdisown\b/;
|
|
1253
1296
|
BACKGROUND_AMPERSAND_RE = /(?<![&|])\s?&\s*(?:$|#|;)/;
|
|
1297
|
+
KUBECTL_RE = /(?:^|[;\n]|&&|\|\|?|&)\s*kubectl(?:\s|$)/;
|
|
1298
|
+
TERRAFORM_RE = /(?:^|[;\n]|&&|\|\|?|&)\s*(?:terraform|tofu)(?:\s|$)/;
|
|
1299
|
+
AWS_CLI_RE = /(?:^|[;\n]|&&|\|\|?|&)\s*aws(?:\s|$)/;
|
|
1300
|
+
GCLOUD_RE = /(?:^|[;\n]|&&|\|\|?|&)\s*gcloud(?:\s|$)/;
|
|
1301
|
+
AZ_CLI_RE = /(?:^|[;\n]|&&|\|\|?|&)\s*az(?:\s|$)/;
|
|
1302
|
+
HELM_RE = /(?:^|[;\n]|&&|\|\|?|&)\s*helm(?:\s|$)/;
|
|
1303
|
+
GH_PIPELINE_RE = /(?:^|[;\n]|&&|\|\|?|&)\s*gh\s+(?:workflow\s+(?:run|enable|disable)|run\s+(?:rerun|cancel)|pr\s+merge|release\s+(?:create|delete)|cache\s+delete|secret\s+(?:set|delete))\b/;
|
|
1254
1304
|
gitBranchCache = new Map;
|
|
1255
1305
|
READ_LIKE_CMDS = /(?:^|;|&&|\|\||\|)\s*(?:ls|find|cat|head|tail|less|more|wc|file|stat|tree|du)\s/;
|
|
1256
1306
|
SEGMENT_SPLIT_RE = /\s*(?:&&|\|\||\||;)\s*/;
|
|
@@ -1379,6 +1429,111 @@ var init_builtin_policies = __esm(() => {
|
|
|
1379
1429
|
defaultEnabled: true,
|
|
1380
1430
|
category: "Dangerous Commands"
|
|
1381
1431
|
},
|
|
1432
|
+
{
|
|
1433
|
+
name: "block-kubectl",
|
|
1434
|
+
description: "Block kubectl commands (Kubernetes cluster mutations)",
|
|
1435
|
+
fn: blockKubectl,
|
|
1436
|
+
match: { events: ["PreToolUse"], toolNames: ["Bash"] },
|
|
1437
|
+
defaultEnabled: false,
|
|
1438
|
+
category: "Infra Commands",
|
|
1439
|
+
params: {
|
|
1440
|
+
allowPatterns: {
|
|
1441
|
+
type: "string[]",
|
|
1442
|
+
description: "kubectl command patterns to allow, matched token-by-token (e.g. 'kubectl get *', 'kubectl describe *')",
|
|
1443
|
+
default: []
|
|
1444
|
+
}
|
|
1445
|
+
}
|
|
1446
|
+
},
|
|
1447
|
+
{
|
|
1448
|
+
name: "block-terraform",
|
|
1449
|
+
description: "Block terraform and tofu (OpenTofu) commands",
|
|
1450
|
+
fn: blockTerraform,
|
|
1451
|
+
match: { events: ["PreToolUse"], toolNames: ["Bash"] },
|
|
1452
|
+
defaultEnabled: false,
|
|
1453
|
+
category: "Infra Commands",
|
|
1454
|
+
params: {
|
|
1455
|
+
allowPatterns: {
|
|
1456
|
+
type: "string[]",
|
|
1457
|
+
description: "terraform/tofu command patterns to allow (e.g. 'terraform plan', 'terraform validate')",
|
|
1458
|
+
default: []
|
|
1459
|
+
}
|
|
1460
|
+
}
|
|
1461
|
+
},
|
|
1462
|
+
{
|
|
1463
|
+
name: "block-aws-cli",
|
|
1464
|
+
description: "Block aws CLI commands",
|
|
1465
|
+
fn: blockAwsCli,
|
|
1466
|
+
match: { events: ["PreToolUse"], toolNames: ["Bash"] },
|
|
1467
|
+
defaultEnabled: false,
|
|
1468
|
+
category: "Infra Commands",
|
|
1469
|
+
params: {
|
|
1470
|
+
allowPatterns: {
|
|
1471
|
+
type: "string[]",
|
|
1472
|
+
description: "aws CLI command patterns to allow (e.g. 'aws s3 ls *', 'aws sts get-caller-identity')",
|
|
1473
|
+
default: []
|
|
1474
|
+
}
|
|
1475
|
+
}
|
|
1476
|
+
},
|
|
1477
|
+
{
|
|
1478
|
+
name: "block-gcloud",
|
|
1479
|
+
description: "Block gcloud (Google Cloud) CLI commands",
|
|
1480
|
+
fn: blockGcloud,
|
|
1481
|
+
match: { events: ["PreToolUse"], toolNames: ["Bash"] },
|
|
1482
|
+
defaultEnabled: false,
|
|
1483
|
+
category: "Infra Commands",
|
|
1484
|
+
params: {
|
|
1485
|
+
allowPatterns: {
|
|
1486
|
+
type: "string[]",
|
|
1487
|
+
description: "gcloud command patterns to allow (e.g. 'gcloud auth list', 'gcloud config list')",
|
|
1488
|
+
default: []
|
|
1489
|
+
}
|
|
1490
|
+
}
|
|
1491
|
+
},
|
|
1492
|
+
{
|
|
1493
|
+
name: "block-az-cli",
|
|
1494
|
+
description: "Block az (Azure) CLI commands",
|
|
1495
|
+
fn: blockAzCli,
|
|
1496
|
+
match: { events: ["PreToolUse"], toolNames: ["Bash"] },
|
|
1497
|
+
defaultEnabled: false,
|
|
1498
|
+
category: "Infra Commands",
|
|
1499
|
+
params: {
|
|
1500
|
+
allowPatterns: {
|
|
1501
|
+
type: "string[]",
|
|
1502
|
+
description: "az CLI command patterns to allow (e.g. 'az account show', 'az group list')",
|
|
1503
|
+
default: []
|
|
1504
|
+
}
|
|
1505
|
+
}
|
|
1506
|
+
},
|
|
1507
|
+
{
|
|
1508
|
+
name: "block-helm",
|
|
1509
|
+
description: "Block helm commands",
|
|
1510
|
+
fn: blockHelm,
|
|
1511
|
+
match: { events: ["PreToolUse"], toolNames: ["Bash"] },
|
|
1512
|
+
defaultEnabled: false,
|
|
1513
|
+
category: "Infra Commands",
|
|
1514
|
+
params: {
|
|
1515
|
+
allowPatterns: {
|
|
1516
|
+
type: "string[]",
|
|
1517
|
+
description: "helm command patterns to allow (e.g. 'helm list', 'helm status *')",
|
|
1518
|
+
default: []
|
|
1519
|
+
}
|
|
1520
|
+
}
|
|
1521
|
+
},
|
|
1522
|
+
{
|
|
1523
|
+
name: "block-gh-pipeline",
|
|
1524
|
+
description: "Block gh CLI pipeline-trigger subcommands (workflow run, run rerun/cancel, pr merge, release create/delete, cache delete, secret set/delete)",
|
|
1525
|
+
fn: blockGhPipeline,
|
|
1526
|
+
match: { events: ["PreToolUse"], toolNames: ["Bash"] },
|
|
1527
|
+
defaultEnabled: false,
|
|
1528
|
+
category: "Infra Commands",
|
|
1529
|
+
params: {
|
|
1530
|
+
allowPatterns: {
|
|
1531
|
+
type: "string[]",
|
|
1532
|
+
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",
|
|
1533
|
+
default: []
|
|
1534
|
+
}
|
|
1535
|
+
}
|
|
1536
|
+
},
|
|
1382
1537
|
{
|
|
1383
1538
|
name: "block-secrets-write",
|
|
1384
1539
|
description: "Block writing secret key files",
|
|
@@ -1981,8 +2136,9 @@ async function loadCustomHooks(customPoliciesPath, opts) {
|
|
|
1981
2136
|
async function loadAllCustomHooks(customPoliciesPath, opts) {
|
|
1982
2137
|
clearCustomHooks();
|
|
1983
2138
|
const conventionSources = [];
|
|
2139
|
+
const projectRoot = findProjectConfigDir(opts?.sessionCwd ?? process.cwd());
|
|
1984
2140
|
if (customPoliciesPath) {
|
|
1985
|
-
const absPath = isAbsolute(customPoliciesPath) ? customPoliciesPath : resolve4(
|
|
2141
|
+
const absPath = isAbsolute(customPoliciesPath) ? customPoliciesPath : resolve4(projectRoot, customPoliciesPath);
|
|
1986
2142
|
if (existsSync3(absPath)) {
|
|
1987
2143
|
await loadSingleFile(absPath);
|
|
1988
2144
|
} else {
|
|
@@ -1990,7 +2146,7 @@ async function loadAllCustomHooks(customPoliciesPath, opts) {
|
|
|
1990
2146
|
}
|
|
1991
2147
|
}
|
|
1992
2148
|
const hooksBeforeConvention = getCustomHooks().length;
|
|
1993
|
-
const projectDir = resolve4(
|
|
2149
|
+
const projectDir = resolve4(projectRoot, ".failproofai", "policies");
|
|
1994
2150
|
const projectFiles = discoverPolicyFiles(projectDir);
|
|
1995
2151
|
for (const file of projectFiles) {
|
|
1996
2152
|
const hooksBefore = getCustomHooks().length;
|
|
@@ -2045,6 +2201,7 @@ var init_custom_hooks_loader = __esm(() => {
|
|
|
2045
2201
|
init_hook_logger();
|
|
2046
2202
|
init_custom_hooks_registry();
|
|
2047
2203
|
init_loader_utils();
|
|
2204
|
+
init_hooks_config();
|
|
2048
2205
|
CONVENTION_FILE_RE = /policies\.(js|mjs|ts)$/;
|
|
2049
2206
|
});
|
|
2050
2207
|
|
|
@@ -2057,7 +2214,7 @@ import {
|
|
|
2057
2214
|
readdirSync as readdirSync2,
|
|
2058
2215
|
mkdirSync as mkdirSync3,
|
|
2059
2216
|
existsSync as existsSync4,
|
|
2060
|
-
statSync as
|
|
2217
|
+
statSync as statSync3,
|
|
2061
2218
|
unlinkSync
|
|
2062
2219
|
} from "node:fs";
|
|
2063
2220
|
import { join as join3 } from "node:path";
|
|
@@ -2079,7 +2236,7 @@ function acquireLock() {
|
|
|
2079
2236
|
if (e.code !== "EEXIST")
|
|
2080
2237
|
return;
|
|
2081
2238
|
try {
|
|
2082
|
-
const s =
|
|
2239
|
+
const s = statSync3(lockPath);
|
|
2083
2240
|
if (Date.now() - s.mtimeMs > LOCK_STALE_MS) {
|
|
2084
2241
|
writeFileSync2(lockPath, String(process.pid), "utf-8");
|
|
2085
2242
|
return;
|
|
@@ -2171,7 +2328,7 @@ var init_hook_activity_store = __esm(() => {
|
|
|
2171
2328
|
});
|
|
2172
2329
|
|
|
2173
2330
|
// package.json
|
|
2174
|
-
var version2 = "0.0.
|
|
2331
|
+
var version2 = "0.0.7";
|
|
2175
2332
|
var init_package = () => {};
|
|
2176
2333
|
|
|
2177
2334
|
// src/posthog-key.ts
|
|
@@ -2349,7 +2506,7 @@ import {
|
|
|
2349
2506
|
mkdirSync as mkdirSync5,
|
|
2350
2507
|
existsSync as existsSync6,
|
|
2351
2508
|
readFileSync as readFileSync4,
|
|
2352
|
-
statSync as
|
|
2509
|
+
statSync as statSync4,
|
|
2353
2510
|
renameSync as renameSync4,
|
|
2354
2511
|
unlinkSync as unlinkSync3,
|
|
2355
2512
|
readdirSync as readdirSync3,
|
|
@@ -2395,7 +2552,7 @@ function appendToServerQueue(entry) {
|
|
|
2395
2552
|
return;
|
|
2396
2553
|
ensureDir2();
|
|
2397
2554
|
try {
|
|
2398
|
-
if (existsSync6(PENDING_FILE) &&
|
|
2555
|
+
if (existsSync6(PENDING_FILE) && statSync4(PENDING_FILE).size > MAX_QUEUE_BYTES) {
|
|
2399
2556
|
return;
|
|
2400
2557
|
}
|
|
2401
2558
|
} catch {}
|
|
@@ -2408,7 +2565,7 @@ function appendToServerQueue(entry) {
|
|
|
2408
2565
|
}
|
|
2409
2566
|
function queueSizeBytes() {
|
|
2410
2567
|
try {
|
|
2411
|
-
return
|
|
2568
|
+
return statSync4(PENDING_FILE).size;
|
|
2412
2569
|
} catch {
|
|
2413
2570
|
return 0;
|
|
2414
2571
|
}
|
|
@@ -2417,7 +2574,7 @@ function claimPendingBatch() {
|
|
|
2417
2574
|
if (!existsSync6(PENDING_FILE))
|
|
2418
2575
|
return null;
|
|
2419
2576
|
try {
|
|
2420
|
-
const size =
|
|
2577
|
+
const size = statSync4(PENDING_FILE).size;
|
|
2421
2578
|
if (size === 0)
|
|
2422
2579
|
return null;
|
|
2423
2580
|
} catch {
|
|
@@ -4484,7 +4641,7 @@ import { realpathSync as realpathSync2 } from "fs";
|
|
|
4484
4641
|
import { dirname as dirname7, resolve as resolve8 } from "path";
|
|
4485
4642
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
4486
4643
|
// package.json
|
|
4487
|
-
var version = "0.0.
|
|
4644
|
+
var version = "0.0.7";
|
|
4488
4645
|
|
|
4489
4646
|
// bin/failproofai.mjs
|
|
4490
4647
|
if (!process.env.FAILPROOFAI_PACKAGE_ROOT) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "failproofai",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.7",
|
|
4
4
|
"description": "The easiest way to manage policies that keep your AI agents reliable, on-task, and running autonomously — for Claude Code & the Agents SDK",
|
|
5
5
|
"bin": {
|
|
6
6
|
"failproofai": "./dist/cli.mjs"
|