(
absolutePath: string,
input: { [key: string]: unknown },
)
| 1609 | * Returns a PermissionResult - either 'allow' if matched, or 'passthrough' to continue checking. |
| 1610 | */ |
| 1611 | export function checkReadableInternalPath( |
| 1612 | absolutePath: string, |
| 1613 | input: { [key: string]: unknown }, |
| 1614 | ): PermissionResult { |
| 1615 | // SECURITY: Normalize path to prevent traversal bypasses via .. segments |
| 1616 | // This is defense-in-depth; individual helper functions also normalize |
| 1617 | const normalizedPath = normalize(absolutePath) |
| 1618 | |
| 1619 | // Session memory directory |
| 1620 | if (isSessionMemoryPath(normalizedPath)) { |
| 1621 | return { |
| 1622 | behavior: 'allow', |
| 1623 | updatedInput: input, |
| 1624 | decisionReason: { |
| 1625 | type: 'other', |
| 1626 | reason: 'Session memory files are allowed for reading', |
| 1627 | }, |
| 1628 | } |
| 1629 | } |
| 1630 | |
| 1631 | // Project directory (for reading past session memories) |
| 1632 | // Path format: ~/.claude/projects/{sanitized-cwd}/... |
| 1633 | if (isProjectDirPath(normalizedPath)) { |
| 1634 | return { |
| 1635 | behavior: 'allow', |
| 1636 | updatedInput: input, |
| 1637 | decisionReason: { |
| 1638 | type: 'other', |
| 1639 | reason: 'Project directory files are allowed for reading', |
| 1640 | }, |
| 1641 | } |
| 1642 | } |
| 1643 | |
| 1644 | // Plan files for current session |
| 1645 | if (isSessionPlanFile(normalizedPath)) { |
| 1646 | return { |
| 1647 | behavior: 'allow', |
| 1648 | updatedInput: input, |
| 1649 | decisionReason: { |
| 1650 | type: 'other', |
| 1651 | reason: 'Plan files for current session are allowed for reading', |
| 1652 | }, |
| 1653 | } |
| 1654 | } |
| 1655 | |
| 1656 | // Tool results directory (persisted large outputs) |
| 1657 | // Use path separator suffix to prevent path traversal (e.g., tool-results-evil/) |
| 1658 | const toolResultsDir = getToolResultsDir() |
| 1659 | const toolResultsDirWithSep = toolResultsDir.endsWith(sep) |
| 1660 | ? toolResultsDir |
| 1661 | : toolResultsDir + sep |
| 1662 | if ( |
| 1663 | normalizedPath === toolResultsDir || |
| 1664 | normalizedPath.startsWith(toolResultsDirWithSep) |
| 1665 | ) { |
| 1666 | return { |
| 1667 | behavior: 'allow', |
| 1668 | updatedInput: input, |
no test coverage detected