(root: string, relPath: string)
| 135 | } |
| 136 | |
| 137 | async function resolveSafeChildPath(root: string, relPath: string): Promise<string> { |
| 138 | const absRoot = path.resolve(root); |
| 139 | const absPath = resolveChildPath(absRoot, relPath); |
| 140 | const rel = path.relative(absRoot, absPath); |
| 141 | if (rel.length === 0) return absPath; |
| 142 | |
| 143 | const parts = rel.split(path.sep).filter((part) => part.length > 0); |
| 144 | let cursor = absRoot; |
| 145 | for (const part of parts) { |
| 146 | cursor = path.join(cursor, part); |
| 147 | try { |
| 148 | const entry = await lstat(cursor); |
| 149 | if (entry.isSymbolicLink()) { |
| 150 | throw new Error(`path traverses symbolic link: ${path.relative(absRoot, cursor)}`); |
| 151 | } |
| 152 | } catch (err) { |
| 153 | if ((err as NodeJS.ErrnoException).code === 'ENOENT') break; |
| 154 | throw err; |
| 155 | } |
| 156 | } |
| 157 | return absPath; |
| 158 | } |
| 159 | |
| 160 | export interface ScaffoldRequest { |
| 161 | kind: string; |
no test coverage detected