@lazyingart/agintiflow 0.20.234 → 0.20.235
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/package.json +1 -1
- package/scripts/smoke-coding-tools.js +16 -0
- package/src/command-policy.js +31 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lazyingart/agintiflow",
|
|
3
|
-
"version": "0.20.
|
|
3
|
+
"version": "0.20.235",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "AgInTiFlow is a project-aware agent workspace for hybrid wet-dry R&D, hardware-aware intelligence, software automation, and industrial workflows.",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -1074,6 +1074,22 @@ try {
|
|
|
1074
1074
|
documentBuildSequencePolicy.category === "general-shell",
|
|
1075
1075
|
"document build sequence should remain broad trusted shell, not destructive"
|
|
1076
1076
|
);
|
|
1077
|
+
const documentIntegrityBuildPolicy = evaluateCommandPolicy(
|
|
1078
|
+
"set -e; mkdir -p output .verification; echo '== source hashes BEFORE build =='; sha256sum README.md PROJECT_NOTES.md source/budget.csv source/meeting-notes.txt source/style-notes.md | tee .verification/src-before.sha256; echo '== chmod + build =='; chmod +x build.sh scripts/*.py; ./build.sh; echo '== source hashes AFTER build (must match BEFORE) =='; sha256sum README.md PROJECT_NOTES.md source/budget.csv source/meeting-notes.txt source/style-notes.md | tee .verification/src-after.sha256; diff .verification/src-before.sha256 .verification/src-after.sha256 && echo 'SOURCE FILES UNCHANGED (byte-for-byte preserved)'",
|
|
1079
|
+
dockerWorkspacePolicy
|
|
1080
|
+
);
|
|
1081
|
+
assert(
|
|
1082
|
+
documentIntegrityBuildPolicy.allowed,
|
|
1083
|
+
"document integrity build with bounded workspace tee targets should be allowed in trusted Docker mode"
|
|
1084
|
+
);
|
|
1085
|
+
assert(
|
|
1086
|
+
documentIntegrityBuildPolicy.category === "general-shell",
|
|
1087
|
+
"document integrity build should remain broad trusted shell, not destructive"
|
|
1088
|
+
);
|
|
1089
|
+
const externalTeePolicy = evaluateCommandPolicy("tee /etc/aginti-test", dockerWorkspacePolicy);
|
|
1090
|
+
assert(!externalTeePolicy.allowed, "tee outside the workspace should remain blocked");
|
|
1091
|
+
const globTeePolicy = evaluateCommandPolicy("tee reports/*.txt", dockerWorkspacePolicy);
|
|
1092
|
+
assert(!globTeePolicy.allowed, "tee wildcard targets should remain blocked");
|
|
1077
1093
|
const hostGlobChmodPolicy = evaluateCommandPolicy("chmod +x scripts/*.py", hostWorkspacePolicy);
|
|
1078
1094
|
assert(!hostGlobChmodPolicy.allowed, "host workspace chmod globs should require explicit trusted host access");
|
|
1079
1095
|
const recursiveChmodPolicy = evaluateCommandPolicy("chmod -R +x scripts", dockerWorkspacePolicy);
|
package/src/command-policy.js
CHANGED
|
@@ -733,7 +733,7 @@ function classifyBackgroundShell(normalized = "") {
|
|
|
733
733
|
|
|
734
734
|
const SAFE_WORKSPACE_WRITE_PATTERNS = [/^mkdir\s+-p\s+[-\w./]+$/];
|
|
735
735
|
const SAFE_CHMOD_MODE_PATTERN = /^[-+=,rwxugoXst0-7]+$/;
|
|
736
|
-
const
|
|
736
|
+
const SAFE_WORKSPACE_TARGET_LIMIT = 64;
|
|
737
737
|
const SAFE_ENV_ASSIGNMENT_NAMES = new Set(["ANDROID_HOME", "ANDROID_SDK_ROOT", "JAVA_HOME", "GRADLE_USER_HOME", "PATH"]);
|
|
738
738
|
const SAFE_ENV_VALUE_PATTERN = /^[-\w./:@+,%]+$/;
|
|
739
739
|
|
|
@@ -1010,7 +1010,7 @@ function classifyWorkspacePermissionChange(normalized = "") {
|
|
|
1010
1010
|
if (tokens[index] !== "chmod") return null;
|
|
1011
1011
|
const mode = String(tokens[index + 1] || "");
|
|
1012
1012
|
const targets = tokens.slice(index + 2);
|
|
1013
|
-
if (!SAFE_CHMOD_MODE_PATTERN.test(mode) || !targets.length || targets.length >
|
|
1013
|
+
if (!SAFE_CHMOD_MODE_PATTERN.test(mode) || !targets.length || targets.length > SAFE_WORKSPACE_TARGET_LIMIT) {
|
|
1014
1014
|
return null;
|
|
1015
1015
|
}
|
|
1016
1016
|
const unsafeTarget = targets.find((target) => !isSafeWorkspaceChmodTarget(target));
|
|
@@ -1030,6 +1030,33 @@ function classifyWorkspacePermissionChange(normalized = "") {
|
|
|
1030
1030
|
};
|
|
1031
1031
|
}
|
|
1032
1032
|
|
|
1033
|
+
function classifyWorkspaceTee(normalized = "") {
|
|
1034
|
+
if (hasActiveShellExpansion(normalized)) return null;
|
|
1035
|
+
const tokens = tokenizeShellWords(normalized);
|
|
1036
|
+
if (tokens[0] !== "tee") return null;
|
|
1037
|
+
let index = 1;
|
|
1038
|
+
if (["-a", "--append"].includes(tokens[index])) index += 1;
|
|
1039
|
+
if (tokens[index] === "--") index += 1;
|
|
1040
|
+
const targets = tokens.slice(index);
|
|
1041
|
+
if (!targets.length || targets.length > SAFE_WORKSPACE_TARGET_LIMIT) return null;
|
|
1042
|
+
const unsafeTarget = targets.find(
|
|
1043
|
+
(target) => target.includes("*") || !isSafeWorkspaceChmodTarget(target)
|
|
1044
|
+
);
|
|
1045
|
+
if (unsafeTarget) {
|
|
1046
|
+
return {
|
|
1047
|
+
category: "blocked",
|
|
1048
|
+
reason: `tee target must be a bounded literal workspace-relative path: ${unsafeTarget}`,
|
|
1049
|
+
};
|
|
1050
|
+
}
|
|
1051
|
+
return {
|
|
1052
|
+
category: "workspace-write",
|
|
1053
|
+
needsNetwork: false,
|
|
1054
|
+
writesWorkspace: true,
|
|
1055
|
+
virtualWorkspacePath: targets.some((target) => target.startsWith("/workspace/")),
|
|
1056
|
+
reason: `Command writes standard input to ${targets.length} bounded workspace target${targets.length === 1 ? "" : "s"}.`,
|
|
1057
|
+
};
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1033
1060
|
function isInsideDirectory(root, candidate) {
|
|
1034
1061
|
const relative = path.relative(root, candidate);
|
|
1035
1062
|
return relative === "" || (relative && !relative.startsWith("..") && !path.isAbsolute(relative));
|
|
@@ -1427,6 +1454,8 @@ function classifySimpleCommand(normalized) {
|
|
|
1427
1454
|
}
|
|
1428
1455
|
const permissionChangeClassification = classifyWorkspacePermissionChange(normalized);
|
|
1429
1456
|
if (permissionChangeClassification) return permissionChangeClassification;
|
|
1457
|
+
const teeClassification = classifyWorkspaceTee(normalized);
|
|
1458
|
+
if (teeClassification) return teeClassification;
|
|
1430
1459
|
const gitCloneClassification = classifyGitClone(normalized);
|
|
1431
1460
|
if (gitCloneClassification) return gitCloneClassification;
|
|
1432
1461
|
const envExportClassification = classifySafeEnvExport(normalized);
|