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