{"record":{"id":"9692b88e764364dc","repo":"paperclipai/paperclip","slug":"native-runtime-context-entry-outside-bundle","errorCode":"native_runtime_context_entry_outside_bundle","errorMessage":"native_runtime_context_entry_outside_bundle","messagePattern":"native_runtime_context_entry_outside_bundle","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/paperclip-runner/src/backends/runtime-context.ts","lineNumber":22,"sourceCode":"import type { NativeExecutionInput } from \"../contracts/native-execution.js\";\nimport { composeNativeSystemInstructions } from \"../contracts/runtime-context.js\";\n\nexport function nativeSystemInstructions(input: NativeExecutionInput): string {\n  if (!(\"runtimeContext\" in input)) return CODEX_SKILLLESS_BASE_INSTRUCTIONS;\n  const configuredRoot = resolve(\n    input.runtimeContext.instructions.bundle.rootPath,\n  );\n  const bundleRoot = realpathSync(configuredRoot);\n  const entryPath = realpathSync(\n    resolve(configuredRoot, input.runtimeContext.instructions.entryPath),\n  );\n  const pathFromRoot = relative(bundleRoot, entryPath);\n  if (\n    pathFromRoot === \"..\" ||\n    pathFromRoot.startsWith(`..${sep}`) ||\n    isAbsolute(pathFromRoot)\n  ) {\n    throw new Error(\"native_runtime_context_entry_outside_bundle\");\n  }\n  const entry = readFileSync(entryPath, \"utf8\");\n  return composeNativeSystemInstructions(input.runtimeContext, entry);\n}\n\nexport function nativeTaskConstraints(input: NativeExecutionInput): string[] {\n  const finalResponseConstraint =\n    \"Invoke paperclip_finish or paperclip_block exactly once before writing the complete user-facing final response. Use paperclip_finish with yielded and a response_wake continuation only when explicitly waiting for the next response. After the semantic tool succeeds, write that response exactly once and do not call another tool.\";\n  const answeredQuestions = Array.isArray(input.interactionResponses)\n    ? input.interactionResponses.flatMap((response, responseIndex) => {\n        if (\n          response.kind !== \"ask_user_questions\" ||\n          response.response?.status !== \"answered\" ||\n          typeof response.interactionId !== \"string\" ||\n          response.interactionId.trim().length === 0\n        ) {\n          return [];\n        }","sourceCodeStart":4,"sourceCodeEnd":40,"githubUrl":"https://github.com/paperclipai/paperclip/blob/01ad8584922b5d85292b1723cae71fa0d9b07a19/packages/paperclip-runner/src/backends/runtime-context.ts#L4-L40","documentation":"nativeSystemInstructions reads a runtime-context entry file and computes its path relative to the bundle root to compose native system instructions. If the entry path resolves outside the bundle root (relative path starts with '../', equals '..', or is absolute), it throws native_runtime_context_entry_outside_bundle to block reads outside the shipped bundle. This is a path-containment guard, not a validation of file content.","triggerScenarios":"input.runtimeContext entryPath pointing at a file outside bundleRoot: an absolute path, a path with ../ traversal, or a symlink-resolved location outside the bundle.","commonSituations":"Configuring a custom runtime-context entry in a repo checkout instead of the bundled install; dev vs packaged layout mismatch where the entry resolves differently; symlinked node_modules or monorepo workspaces making the relative path cross the bundle boundary; user-supplied path containing '..'.","solutions":["Place the runtime-context entry inside bundleRoot (or copy it in) and reference it relatively","Check with path.relative before calling: ensure the result does not start with '..' and is not absolute","Fix packaging so the entry ships inside the bundle (update build/pack config)","Resolve symlinks (fs.realpathSync) and verify containment before invoking"],"exampleFix":"// before\nawait nativeSystemInstructions({ runtimeContext: { entryPath: '/etc/agent/context.md', ... } });\n// after\nconst rel = path.relative(bundleRoot, entryPath);\nif (rel.startsWith('..') || path.isAbsolute(rel)) {\n  entryPath = path.join(bundleRoot, 'runtime-context', 'context.md'); // copy asset into bundle\n}\nawait nativeSystemInstructions({ runtimeContext: { entryPath, ... } });","handlingStrategy":"validation","validationCode":"const rel = path.relative(bundleRoot, entryPath);\nif (rel === '..' || rel.startsWith('..' + path.sep) || path.isAbsolute(rel)) throw new Error('entry outside bundle');","typeGuard":"function isInsideBundle(bundleRoot: string, entryPath: string): boolean {\n  const rel = path.relative(bundleRoot, path.resolve(entryPath));\n  return rel !== '..' && !rel.startsWith('..' + path.sep) && !path.isAbsolute(rel);\n}","tryCatchPattern":"try { return nativeSystemInstructions(input); } catch (e) { if (e.message === 'native_runtime_context_entry_outside_bundle') { return composeWithBundledFallback(input); } throw e; }","preventionTips":["Ship runtime-context entries inside the bundle at build time","Reject user-supplied entry paths containing '..' or absolute paths","Resolve symlinks and re-check containment before use","Test in packaged (not just dev) layouts"],"tags":["path-traversal","security","filesystem"],"backgroundTag":"path-traversal-blocked","analyzedSha":"01ad8584922b5d85292b1723cae71fa0d9b07a19","analyzedAt":"2026-09-10T03:14:50.855Z","contentChangedAt":"2026-09-10T03:14:50.855Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}