@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lazyingart/agintiflow",
3
- "version": "0.20.234",
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);
@@ -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 SAFE_CHMOD_TARGET_LIMIT = 64;
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 > SAFE_CHMOD_TARGET_LIMIT) {
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);