@lazyingart/agintiflow 0.20.233 → 0.20.234
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 +18 -0
- package/src/command-policy.js +55 -16
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lazyingart/agintiflow",
|
|
3
|
-
"version": "0.20.
|
|
3
|
+
"version": "0.20.234",
|
|
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",
|
|
@@ -1062,6 +1062,24 @@ try {
|
|
|
1062
1062
|
assert(safeChmodAndRunPolicy.allowed, "safe workspace chmod + script run sequence should be allowed in docker-workspace allow mode");
|
|
1063
1063
|
const unsafeChmodPolicy = evaluateCommandPolicy("chmod +x /etc/passwd", dockerWorkspacePolicy);
|
|
1064
1064
|
assert(!unsafeChmodPolicy.allowed, "chmod outside the workspace should be blocked");
|
|
1065
|
+
const documentBuildSequencePolicy = evaluateCommandPolicy(
|
|
1066
|
+
"sha256sum README.md PROJECT_NOTES.md source/budget.csv source/meeting-notes.txt source/style-notes.md > /tmp/src-before.sha256 && cat /tmp/src-before.sha256 && echo '---' && chmod +x build.sh scripts/*.py && ./build.sh",
|
|
1067
|
+
dockerWorkspacePolicy
|
|
1068
|
+
);
|
|
1069
|
+
assert(
|
|
1070
|
+
documentBuildSequencePolicy.allowed,
|
|
1071
|
+
"bounded document build sequence with workspace-local chmod glob should be allowed in trusted Docker mode"
|
|
1072
|
+
);
|
|
1073
|
+
assert(
|
|
1074
|
+
documentBuildSequencePolicy.category === "general-shell",
|
|
1075
|
+
"document build sequence should remain broad trusted shell, not destructive"
|
|
1076
|
+
);
|
|
1077
|
+
const hostGlobChmodPolicy = evaluateCommandPolicy("chmod +x scripts/*.py", hostWorkspacePolicy);
|
|
1078
|
+
assert(!hostGlobChmodPolicy.allowed, "host workspace chmod globs should require explicit trusted host access");
|
|
1079
|
+
const recursiveChmodPolicy = evaluateCommandPolicy("chmod -R +x scripts", dockerWorkspacePolicy);
|
|
1080
|
+
assert(!recursiveChmodPolicy.allowed, "recursive chmod should remain outside the bounded permission-change policy");
|
|
1081
|
+
const parentTraversalChmodPolicy = evaluateCommandPolicy("chmod +x scripts/../outside.py", dockerWorkspacePolicy);
|
|
1082
|
+
assert(!parentTraversalChmodPolicy.allowed, "chmod parent traversal should remain blocked");
|
|
1065
1083
|
const hostWorkspaceChmodPolicy = evaluateCommandPolicy("chmod +x android-app/gradlew && echo \"CHMOD_OK\"", hostWorkspacePolicy);
|
|
1066
1084
|
assert(hostWorkspaceChmodPolicy.allowed, "host mode should allow workspace-local chmod without full-host destructive access");
|
|
1067
1085
|
assert(
|
package/src/command-policy.js
CHANGED
|
@@ -732,7 +732,8 @@ function classifyBackgroundShell(normalized = "") {
|
|
|
732
732
|
}
|
|
733
733
|
|
|
734
734
|
const SAFE_WORKSPACE_WRITE_PATTERNS = [/^mkdir\s+-p\s+[-\w./]+$/];
|
|
735
|
-
const
|
|
735
|
+
const SAFE_CHMOD_MODE_PATTERN = /^[-+=,rwxugoXst0-7]+$/;
|
|
736
|
+
const SAFE_CHMOD_TARGET_LIMIT = 64;
|
|
736
737
|
const SAFE_ENV_ASSIGNMENT_NAMES = new Set(["ANDROID_HOME", "ANDROID_SDK_ROOT", "JAVA_HOME", "GRADLE_USER_HOME", "PATH"]);
|
|
737
738
|
const SAFE_ENV_VALUE_PATTERN = /^[-\w./:@+,%]+$/;
|
|
738
739
|
|
|
@@ -983,6 +984,52 @@ function isSafeWorkspacePath(value) {
|
|
|
983
984
|
return isSafeRelativeDir(value) || isSafeVirtualWorkspacePath(value);
|
|
984
985
|
}
|
|
985
986
|
|
|
987
|
+
function isSafeWorkspaceChmodTarget(value = "") {
|
|
988
|
+
const normalized = String(value || "").trim();
|
|
989
|
+
if (!normalized || normalized.startsWith("-") || normalized.startsWith("~")) return false;
|
|
990
|
+
const relative = normalized.startsWith("/workspace/")
|
|
991
|
+
? normalized.replace(/^\/workspace\//, "")
|
|
992
|
+
: normalized;
|
|
993
|
+
if (!relative || relative.startsWith("/") || !/^[A-Za-z0-9_@%+=,:.*\/-]+$/.test(relative)) {
|
|
994
|
+
return false;
|
|
995
|
+
}
|
|
996
|
+
const parts = relative.split("/");
|
|
997
|
+
if (parts.some((part) => !part || part === "." || part === "..")) return false;
|
|
998
|
+
const globParts = parts.filter((part) => part.includes("*"));
|
|
999
|
+
if (!globParts.length) return isSafeWorkspacePath(normalized);
|
|
1000
|
+
if (globParts.length !== 1 || !parts.at(-1)?.includes("*")) return false;
|
|
1001
|
+
const leaf = parts.at(-1) || "";
|
|
1002
|
+
return !leaf.includes("**") && /^[A-Za-z0-9_.-]*\*[A-Za-z0-9_.-]*$/.test(leaf);
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
function classifyWorkspacePermissionChange(normalized = "") {
|
|
1006
|
+
if (hasActiveShellExpansion(normalized)) return null;
|
|
1007
|
+
const tokens = tokenizeShellWords(normalized);
|
|
1008
|
+
let index = 0;
|
|
1009
|
+
if (tokens[index] === "sudo") index += 1;
|
|
1010
|
+
if (tokens[index] !== "chmod") return null;
|
|
1011
|
+
const mode = String(tokens[index + 1] || "");
|
|
1012
|
+
const targets = tokens.slice(index + 2);
|
|
1013
|
+
if (!SAFE_CHMOD_MODE_PATTERN.test(mode) || !targets.length || targets.length > SAFE_CHMOD_TARGET_LIMIT) {
|
|
1014
|
+
return null;
|
|
1015
|
+
}
|
|
1016
|
+
const unsafeTarget = targets.find((target) => !isSafeWorkspaceChmodTarget(target));
|
|
1017
|
+
if (unsafeTarget) {
|
|
1018
|
+
return {
|
|
1019
|
+
category: "blocked",
|
|
1020
|
+
reason: `chmod target must be a bounded workspace-relative path: ${unsafeTarget}`,
|
|
1021
|
+
};
|
|
1022
|
+
}
|
|
1023
|
+
return {
|
|
1024
|
+
category: "permission-change",
|
|
1025
|
+
needsNetwork: false,
|
|
1026
|
+
writesWorkspace: true,
|
|
1027
|
+
virtualWorkspacePath: targets.some((target) => target.startsWith("/workspace/")),
|
|
1028
|
+
permissionTargetsContainGlob: targets.some((target) => target.includes("*")),
|
|
1029
|
+
reason: `Command changes workspace file mode for ${targets.length} bounded target${targets.length === 1 ? "" : "s"}.`,
|
|
1030
|
+
};
|
|
1031
|
+
}
|
|
1032
|
+
|
|
986
1033
|
function isInsideDirectory(root, candidate) {
|
|
987
1034
|
const relative = path.relative(root, candidate);
|
|
988
1035
|
return relative === "" || (relative && !relative.startsWith("..") && !path.isAbsolute(relative));
|
|
@@ -1378,20 +1425,8 @@ function classifySimpleCommand(normalized) {
|
|
|
1378
1425
|
}
|
|
1379
1426
|
return { category: "workspace-write", needsNetwork: false, writesWorkspace: true, virtualWorkspacePath };
|
|
1380
1427
|
}
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
const virtualWorkspacePath = isSafeVirtualWorkspacePath(target);
|
|
1384
|
-
if (!isSafeWorkspacePath(target)) {
|
|
1385
|
-
return { category: "blocked", reason: `chmod target must be a safe workspace-relative path: ${target}` };
|
|
1386
|
-
}
|
|
1387
|
-
return {
|
|
1388
|
-
category: "permission-change",
|
|
1389
|
-
needsNetwork: false,
|
|
1390
|
-
writesWorkspace: true,
|
|
1391
|
-
virtualWorkspacePath,
|
|
1392
|
-
reason: `Command changes workspace file mode: ${normalized}`,
|
|
1393
|
-
};
|
|
1394
|
-
}
|
|
1428
|
+
const permissionChangeClassification = classifyWorkspacePermissionChange(normalized);
|
|
1429
|
+
if (permissionChangeClassification) return permissionChangeClassification;
|
|
1395
1430
|
const gitCloneClassification = classifyGitClone(normalized);
|
|
1396
1431
|
if (gitCloneClassification) return gitCloneClassification;
|
|
1397
1432
|
const envExportClassification = classifySafeEnvExport(normalized);
|
|
@@ -2553,7 +2588,11 @@ export function evaluateCommandPolicy(command, config = {}) {
|
|
|
2553
2588
|
};
|
|
2554
2589
|
}
|
|
2555
2590
|
|
|
2556
|
-
if (
|
|
2591
|
+
if (
|
|
2592
|
+
classification.category === "permission-change" &&
|
|
2593
|
+
((sandboxMode !== "host" && !trustedDockerShell && !trustedHostShell) ||
|
|
2594
|
+
(sandboxMode === "host" && classification.permissionTargetsContainGlob && !trustedHostShell))
|
|
2595
|
+
) {
|
|
2557
2596
|
return {
|
|
2558
2597
|
allowed: false,
|
|
2559
2598
|
...classification,
|