@foldrun/core 1.4.0 → 1.4.2
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/dist/src/deploy.js +3 -1
- package/dist/src/run-container.js +11 -0
- package/dist/src/scan.js +9 -0
- package/package.json +1 -1
- package/src/deploy.ts +3 -1
- package/src/run-container.ts +10 -0
- package/src/scan.ts +9 -0
package/dist/src/deploy.js
CHANGED
|
@@ -36,7 +36,9 @@ import { repoDir } from "./gitrepo.js";
|
|
|
36
36
|
import { entriesNoFollow } from "./paths.js";
|
|
37
37
|
import { filterIgnored, findingFingerprint, ruleForWhere, scanFiles } from "./scan.js";
|
|
38
38
|
/** Directories a deploy never reads out of a source tree. */
|
|
39
|
-
|
|
39
|
+
// `.cache` is what a run's tools leave in an agent's folder (HOME is that
|
|
40
|
+
// folder): logs and font caches, never authored.
|
|
41
|
+
const NOT_SOURCE = new Set([".git", "node_modules", "runs", "outputs", ".foldrun", ".results", ".cache"]);
|
|
40
42
|
/** Files that are never part of a workspace, whatever directory they sit in. */
|
|
41
43
|
const NOT_SOURCE_FILE = new Set([".DS_Store", "secrets.json"]);
|
|
42
44
|
const IN_WORKSPACE_DIR = new RegExp(`^(${WORKSPACE_DIRS.join("|")})/`);
|
|
@@ -59,6 +59,10 @@ const DENY_BACK_GIT_RE = /(^|\/)\.git(\/|$)/;
|
|
|
59
59
|
const DENY_BACK_SECRET_FILES = /(^|\/)\.secret-files\//;
|
|
60
60
|
// And per-agent knowledge, at any depth under agents/<name>/.
|
|
61
61
|
const DENY_BACK_RE = /^agents\/[^/]+\/knowledge\//;
|
|
62
|
+
// A run's caches are not workspace content either: HOME is the agent's own
|
|
63
|
+
// folder, so a tool that ignores XDG_CACHE_HOME (set by the driver) still
|
|
64
|
+
// writes ~/.cache or ~/.npm there. Never copied back.
|
|
65
|
+
const DENY_BACK_CACHE = /^agents\/[^/]+\/\.(?:cache|npm)(?:\/|$)/;
|
|
62
66
|
// A dependency tree is not workspace content. Nothing authored lives in
|
|
63
67
|
// node_modules, it is enormous, and a runtime linked into a tool's directory
|
|
64
68
|
// so ESM can resolve it would otherwise be copied back file by file.
|
|
@@ -80,6 +84,8 @@ export function allowedBack(rel) {
|
|
|
80
84
|
return false;
|
|
81
85
|
if (DENY_BACK_NODE_MODULES.test(norm))
|
|
82
86
|
return false;
|
|
87
|
+
if (DENY_BACK_CACHE.test(norm))
|
|
88
|
+
return false;
|
|
83
89
|
return true;
|
|
84
90
|
}
|
|
85
91
|
/**
|
|
@@ -433,6 +439,11 @@ try {
|
|
|
433
439
|
// @file secrets crossed as content; materialise them to 0600 paths inside
|
|
434
440
|
// the container (a host path from the other side would not exist here).
|
|
435
441
|
const env = materializeFileSecrets(agentDir, { ...process.env, HOME: agentDir }).env;
|
|
442
|
+
// Caches go to the sandbox user's own home, not to HOME: the Claude CLI's
|
|
443
|
+
// logs, fontconfig, Mesa and gstreamer all follow XDG_CACHE_HOME, and under
|
|
444
|
+
// agents/<name>/.cache they were copied back after every step — 3,855 files
|
|
445
|
+
// across 179 agent folders on the box by 17 Oct 2026, read by nothing.
|
|
446
|
+
env.XDG_CACHE_HOME = "/home/agent/.cache";
|
|
436
447
|
|
|
437
448
|
// The declared runtime, built in here — pip and npm have the network, which
|
|
438
449
|
// the host side may not. It lands in .foldrun/runner/.runtimes, which the
|
package/dist/src/scan.js
CHANGED
|
@@ -646,6 +646,13 @@ function ignored(ig, rule, line) {
|
|
|
646
646
|
/** Installed and built code: someone else's files, and a crypto library's
|
|
647
647
|
* constants are not this workspace's keys. */
|
|
648
648
|
const VENDORED = /(?:^|\/)(?:node_modules|\.venv|venv|site-packages|__pycache__|vendor|dist|build|\.next)\//;
|
|
649
|
+
/** What a run leaves in an agent's folder — the Claude CLI's `.cache/` logs,
|
|
650
|
+
* package caches — and copies back with it. Nobody wrote these, so they are
|
|
651
|
+
* not checked as prompts or code; they are still searched for credentials,
|
|
652
|
+
* since a key in a log here is a key in the workspace (seen live on
|
|
653
|
+
* 17 Oct 2026: zero-width characters in a CLI MCP log, reported as hidden
|
|
654
|
+
* text). */
|
|
655
|
+
const RUNTIME_WRITTEN = /(?:^|\/)(?:\.cache|\.npm|\.local|\.pnpm-store|\.yarn|\.foldrun)\//;
|
|
649
656
|
/** Text files only — a PNG read as UTF-8 is noise to every rule here. */
|
|
650
657
|
function isText(f) {
|
|
651
658
|
return !VENDORED.test(f.path) && !f.content.slice(0, 8000).includes("\u0000") && !/\.(?:png|jpe?g|gif|webp|ico|pdf|zip|gz|woff2?|ttf|mp[34]|mov|wasm)$/i.test(f.path);
|
|
@@ -679,6 +686,8 @@ export function scanFiles(files, options = {}) {
|
|
|
679
686
|
const lines = f.content.split(/\r?\n/);
|
|
680
687
|
ignoresByFile.set(f.path, readIgnores(f, lines, push));
|
|
681
688
|
scanSecrets(f, lines, push);
|
|
689
|
+
if (RUNTIME_WRITTEN.test(f.path))
|
|
690
|
+
continue;
|
|
682
691
|
scanContent(f, lines, push);
|
|
683
692
|
scanSupplyChain(f, lines, push);
|
|
684
693
|
const schema = schemaKindOf(f.path);
|
package/package.json
CHANGED
package/src/deploy.ts
CHANGED
|
@@ -49,7 +49,9 @@ import { entriesNoFollow } from "./paths.ts";
|
|
|
49
49
|
import { filterIgnored, findingFingerprint, ruleForWhere, scanFiles, type ScanFinding, type ScanOptions } from "./scan.ts";
|
|
50
50
|
|
|
51
51
|
/** Directories a deploy never reads out of a source tree. */
|
|
52
|
-
|
|
52
|
+
// `.cache` is what a run's tools leave in an agent's folder (HOME is that
|
|
53
|
+
// folder): logs and font caches, never authored.
|
|
54
|
+
const NOT_SOURCE = new Set([".git", "node_modules", "runs", "outputs", ".foldrun", ".results", ".cache"]);
|
|
53
55
|
|
|
54
56
|
/** Files that are never part of a workspace, whatever directory they sit in. */
|
|
55
57
|
const NOT_SOURCE_FILE = new Set([".DS_Store", "secrets.json"]);
|
package/src/run-container.ts
CHANGED
|
@@ -192,6 +192,10 @@ const DENY_BACK_GIT_RE = /(^|\/)\.git(\/|$)/;
|
|
|
192
192
|
const DENY_BACK_SECRET_FILES = /(^|\/)\.secret-files\//;
|
|
193
193
|
// And per-agent knowledge, at any depth under agents/<name>/.
|
|
194
194
|
const DENY_BACK_RE = /^agents\/[^/]+\/knowledge\//;
|
|
195
|
+
// A run's caches are not workspace content either: HOME is the agent's own
|
|
196
|
+
// folder, so a tool that ignores XDG_CACHE_HOME (set by the driver) still
|
|
197
|
+
// writes ~/.cache or ~/.npm there. Never copied back.
|
|
198
|
+
const DENY_BACK_CACHE = /^agents\/[^/]+\/\.(?:cache|npm)(?:\/|$)/;
|
|
195
199
|
// A dependency tree is not workspace content. Nothing authored lives in
|
|
196
200
|
// node_modules, it is enormous, and a runtime linked into a tool's directory
|
|
197
201
|
// so ESM can resolve it would otherwise be copied back file by file.
|
|
@@ -207,6 +211,7 @@ export function allowedBack(rel: string): boolean {
|
|
|
207
211
|
if (DENY_BACK_GIT_RE.test(norm)) return false;
|
|
208
212
|
if (DENY_BACK_SECRET_FILES.test(norm)) return false;
|
|
209
213
|
if (DENY_BACK_NODE_MODULES.test(norm)) return false;
|
|
214
|
+
if (DENY_BACK_CACHE.test(norm)) return false;
|
|
210
215
|
return true;
|
|
211
216
|
}
|
|
212
217
|
|
|
@@ -564,6 +569,11 @@ try {
|
|
|
564
569
|
// @file secrets crossed as content; materialise them to 0600 paths inside
|
|
565
570
|
// the container (a host path from the other side would not exist here).
|
|
566
571
|
const env = materializeFileSecrets(agentDir, { ...process.env, HOME: agentDir }).env;
|
|
572
|
+
// Caches go to the sandbox user's own home, not to HOME: the Claude CLI's
|
|
573
|
+
// logs, fontconfig, Mesa and gstreamer all follow XDG_CACHE_HOME, and under
|
|
574
|
+
// agents/<name>/.cache they were copied back after every step — 3,855 files
|
|
575
|
+
// across 179 agent folders on the box by 17 Oct 2026, read by nothing.
|
|
576
|
+
env.XDG_CACHE_HOME = "/home/agent/.cache";
|
|
567
577
|
|
|
568
578
|
// The declared runtime, built in here — pip and npm have the network, which
|
|
569
579
|
// the host side may not. It lands in .foldrun/runner/.runtimes, which the
|
package/src/scan.ts
CHANGED
|
@@ -729,6 +729,14 @@ type Push = (rule: string, file: string, line: number | undefined, text: string,
|
|
|
729
729
|
* constants are not this workspace's keys. */
|
|
730
730
|
const VENDORED = /(?:^|\/)(?:node_modules|\.venv|venv|site-packages|__pycache__|vendor|dist|build|\.next)\//;
|
|
731
731
|
|
|
732
|
+
/** What a run leaves in an agent's folder — the Claude CLI's `.cache/` logs,
|
|
733
|
+
* package caches — and copies back with it. Nobody wrote these, so they are
|
|
734
|
+
* not checked as prompts or code; they are still searched for credentials,
|
|
735
|
+
* since a key in a log here is a key in the workspace (seen live on
|
|
736
|
+
* 17 Oct 2026: zero-width characters in a CLI MCP log, reported as hidden
|
|
737
|
+
* text). */
|
|
738
|
+
const RUNTIME_WRITTEN = /(?:^|\/)(?:\.cache|\.npm|\.local|\.pnpm-store|\.yarn|\.foldrun)\//;
|
|
739
|
+
|
|
732
740
|
/** Text files only — a PNG read as UTF-8 is noise to every rule here. */
|
|
733
741
|
function isText(f: ScanFile): boolean {
|
|
734
742
|
return !VENDORED.test(f.path) && !f.content.slice(0, 8000).includes("\u0000") && !/\.(?:png|jpe?g|gif|webp|ico|pdf|zip|gz|woff2?|ttf|mp[34]|mov|wasm)$/i.test(f.path);
|
|
@@ -763,6 +771,7 @@ export function scanFiles(files: ScanFile[], options: ScanOptions = {}): ScanFin
|
|
|
763
771
|
const lines = f.content.split(/\r?\n/);
|
|
764
772
|
ignoresByFile.set(f.path, readIgnores(f, lines, push));
|
|
765
773
|
scanSecrets(f, lines, push);
|
|
774
|
+
if (RUNTIME_WRITTEN.test(f.path)) continue;
|
|
766
775
|
scanContent(f, lines, push);
|
|
767
776
|
scanSupplyChain(f, lines, push);
|
|
768
777
|
|