{"record":{"id":"9d0bce07b0d6bc26","repo":"coleam00/Archon","slug":"artifacts-path-escape-blocked","errorCode":"artifacts.path_escape_blocked","errorMessage":"Invalid artifact path","messagePattern":"Invalid artifact path","errorType":"http","errorClass":null,"httpStatus":400,"severity":"error","filePath":"packages/server/src/routes/api.ts","lineNumber":4698,"sourceCode":"          { err: error, runId, codebaseId: run.codebase_id },\n          'artifacts.codebase_lookup_failed'\n        );\n        return apiError(c, 500, 'Failed to look up codebase');\n      }\n    }\n    // An empty 200 here is indistinguishable from \"the run produced nothing\",\n    // so an unresolvable output location is an explicit 404 (Fail Fast).\n    const artifactDir = resolveRunArtifactDir(run, codebase, runId);\n    if (!artifactDir) {\n      getLog().warn({ runId, codebaseId: run.codebase_id }, 'artifacts.output_location_unresolved');\n      return apiError(\n        c,\n        404,\n        'Artifacts not available: could not resolve this run’s output location'\n      );\n    }\n    if (!isInsideArchonHome(artifactDir)) {\n      getLog().warn(\n        { runId, artifactDir, archonHome: getArchonHome() },\n        'artifacts.path_escape_blocked'\n      );\n      return apiError(c, 400, 'Invalid artifact path');\n    }\n\n    interface FileEntry {\n      path: string;\n      size: number;\n      modifiedAt: string;\n    }\n    const files: FileEntry[] = [];\n\n    async function walk(dir: string, rel: string): Promise<void> {\n      let entries: { name: string; isDirectory: () => boolean; isFile: () => boolean }[];\n      try {\n        entries = await readdir(dir, { withFileTypes: true });\n      } catch (err) {","sourceCodeStart":4680,"sourceCodeEnd":4716,"githubUrl":"https://github.com/coleam00/Archon/blob/0773b9745896ef0612e709c80845a0f7db315b19/packages/server/src/routes/api.ts#L4680-L4716","documentation":"The artifacts listing endpoint resolved a run's artifact directory, but isInsideArchonHome(artifactDir) returned false — the path points outside the configured Archon home. This is a path-escape guard returning 400 to prevent serving files from arbitrary filesystem locations.","triggerScenarios":"GET run artifacts where resolveRunArtifactDir returns a directory outside getArchonHome() — typically because ARCHON_HOME changed between runs or the stored run/codebase path references a foreign location.","commonSituations":"Moving or renaming the Archon home directory while old runs persist; restored database referencing paths from another machine; misconfigured ARCHON_HOME env var.","solutions":["Point ARCHON_HOME back to the location that contains the run's artifacts, or migrate the artifact directories under the current home","Inspect the warn log's artifactDir vs archonHome values to see exactly where the mismatch is","Re-run the workflow under the current configuration so artifacts land inside the home","Do not bypass the guard; fix the path configuration instead"],"exampleFix":"null","handlingStrategy":"validation","validationCode":"import { isInsideArchonHome } from '@archon/paths';\n// pre-check client-side of deployment config:\nconst dir = resolveRunArtifactDir(run, codebase, runId);\nif (dir && !isInsideArchonHome(dir)) throw new Error(`artifact dir ${dir} escapes ARCHON_HOME ${getArchonHome()}`);","typeGuard":"function artifactDirIsSafe(dir: string | null, isInside: (p: string) => boolean): dir is string {\n  return typeof dir === 'string' && isInside(dir);\n}","tryCatchPattern":"null","preventionTips":["Keep ARCHON_HOME stable across restarts; if it moves, migrate existing artifact directories into the new home","Never hand-edit stored run/codebase paths in the database","After database restores from other hosts, audit stored paths against the local ARCHON_HOME","Check the path_escape_blocked log's artifactDir vs archonHome to diagnose misconfiguration quickly"],"tags":["api","artifacts","security","path-traversal","server"],"backgroundTag":"path-traversal-blocked","analyzedSha":"0773b9745896ef0612e709c80845a0f7db315b19","analyzedAt":"2026-09-01T02:28:07.064Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}