* Check whether a real (symlink-resolved) path is within the real team * memory directory. Both sides are realpath'd so the comparison is between * canonical filesystem locations. * * If teamDir does not exist, returns true (skips the check). This is safe: * a symlink escape requires a pre-exis
( realCandidate: string, )
| 181 | * and the first-pass string-level containment check is sufficient. |
| 182 | */ |
| 183 | async function isRealPathWithinTeamDir( |
| 184 | realCandidate: string, |
| 185 | ): Promise<boolean> { |
| 186 | let realTeamDir: string |
| 187 | try { |
| 188 | // getTeamMemPath() includes a trailing separator; strip it because |
| 189 | // realpath() rejects trailing separators on some platforms. |
| 190 | realTeamDir = await realpath(getTeamMemPath().replace(/[/\\]+$/, '')) |
| 191 | } catch (e: unknown) { |
| 192 | const code = getErrnoCode(e) |
| 193 | if (code === 'ENOENT' || code === 'ENOTDIR') { |
| 194 | // Team dir doesn't exist — symlink escape impossible, skip check. |
| 195 | return true |
| 196 | } |
| 197 | // Unexpected error (EACCES, EIO) — fail closed. |
| 198 | return false |
| 199 | } |
| 200 | if (realCandidate === realTeamDir) { |
| 201 | return true |
| 202 | } |
| 203 | // Prefix-attack protection: require separator after the prefix so that |
| 204 | // "/foo/team-evil" doesn't match "/foo/team". |
| 205 | return realCandidate.startsWith(realTeamDir + sep) |
| 206 | } |
| 207 | |
| 208 | /** |
| 209 | * Check if a resolved absolute path is within the team memory directory. |
no test coverage detected