MCPcopy Index your code
hub / github.com/codeaashu/claude-code / checkEditableInternalPath

Function checkEditableInternalPath

src/utils/permissions/filesystem.ts:1479–1605  ·  view source on GitHub ↗
(
  absolutePath: string,
  input: { [key: string]: unknown },
)

Source from the content-addressed store, hash-verified

1477 * Returns a PermissionResult - either 'allow' if matched, or 'passthrough' to continue checking.
1478 */
1479export function checkEditableInternalPath(
1480 absolutePath: string,
1481 input: { [key: string]: unknown },
1482): PermissionResult {
1483 // SECURITY: Normalize path to prevent traversal bypasses via .. segments
1484 // This is defense-in-depth; individual helper functions also normalize
1485 const normalizedPath = normalize(absolutePath)
1486
1487 // Plan files for current session
1488 if (isSessionPlanFile(normalizedPath)) {
1489 return {
1490 behavior: 'allow',
1491 updatedInput: input,
1492 decisionReason: {
1493 type: 'other',
1494 reason: 'Plan files for current session are allowed for writing',
1495 },
1496 }
1497 }
1498
1499 // Scratchpad directory for current session
1500 if (isScratchpadPath(normalizedPath)) {
1501 return {
1502 behavior: 'allow',
1503 updatedInput: input,
1504 decisionReason: {
1505 type: 'other',
1506 reason: 'Scratchpad files for current session are allowed for writing',
1507 },
1508 }
1509 }
1510
1511 // Template job's own directory. Env key hardcoded (vs importing JOB_ENV_KEY
1512 // from jobs/state) so tree-shaking eliminates the string from external
1513 // builds — spawn.test.ts asserts the string matches. Hijack guard: the env
1514 // var value must itself resolve under ~/.claude/jobs/. Symlink guard: every
1515 // resolved form of the target (lexical + symlink chain) must fall under some
1516 // resolved form of the job dir, so a symlink inside the job dir pointing at
1517 // e.g. ~/.ssh/authorized_keys does not get a free write. Resolving both
1518 // sides handles the macOS /tmp → /private/tmp case where the config dir
1519 // lives under a symlinked root.
1520 if (feature('TEMPLATES')) {
1521 const jobDir = process.env.CLAUDE_JOB_DIR
1522 if (jobDir) {
1523 const jobsRoot = join(getClaudeConfigHomeDir(), 'jobs')
1524 const jobDirForms = getPathsForPermissionCheck(jobDir).map(normalize)
1525 const jobsRootForms = getPathsForPermissionCheck(jobsRoot).map(normalize)
1526 // Hijack guard: every resolved form of the job dir must sit under
1527 // some resolved form of the jobs root. Resolving both sides handles
1528 // the case where ~/.claude is a symlink (e.g. to /data/claude-config).
1529 const isUnderJobsRoot = jobDirForms.every(jd =>
1530 jobsRootForms.some(jr => jd.startsWith(jr + sep)),
1531 )
1532 if (isUnderJobsRoot) {
1533 const targetForms = getPathsForPermissionCheck(absolutePath)
1534 const allInsideJobDir = targetForms.every(p => {
1535 const np = normalize(p)
1536 return jobDirForms.some(jd => np === jd || np.startsWith(jd + sep))

Callers 3

isPathAllowedFunction · 0.85
isPathAllowedFunction · 0.85

Calls 10

isSessionPlanFileFunction · 0.85
isScratchpadPathFunction · 0.85
featureFunction · 0.85
isAgentMemoryPathFunction · 0.85
hasAutoMemPathOverrideFunction · 0.85
isAutoMemPathFunction · 0.85
getOriginalCwdFunction · 0.85
normalizeFunction · 0.50

Tested by

no test coverage detected