Skip to content

Commit 8e5f2b3

Browse files
authored
fix: sanitize the branch name returned by the github api (#43)
The remaining tssecurity:S8476 flow is a different one from the two fixed in #42: its source is not user input but the GitHub API response itself. `fetchDefaultBranch` trusted `data.default_branch` verbatim and that value flows straight back into the trees and contents request URLs, so a compromised or unexpected response could steer them. `default_branch` now goes through the same `sanitizeBranch` ref check as user input and falls back to 'main' when it fails. `fetchFile` sanitizes its branch argument too, so all three request builders agree instead of `fetchTree` being the only one that checked. Verified with the real module and fetch stubbed to answer the repo lookup with default_branch of "../../../evil?x=": 8 requests issued, 0 escaping the /repos/<owner>/<repo> path, tree call falls back to main.
1 parent 6423ede commit 8e5f2b3

1 file changed

Lines changed: 6 additions & 2 deletions

File tree

site/src/utils/repoAnalyzer.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,8 @@ async function fetchFile(
144144
path: string,
145145
branch: string,
146146
): Promise<FetchedFile> {
147-
const ref = branch ? `?ref=${encodeURIComponent(branch)}` : ''
147+
const safeBranch = sanitizeBranch(branch)
148+
const ref = safeBranch ? `?ref=${encodeURIComponent(safeBranch)}` : ''
148149
const encodedPath = path.split('/').map(encodeURIComponent).join('/')
149150
const url = buildApiUrl(owner, repo, `/contents/${encodedPath}${ref}`)
150151
if (!url) {
@@ -177,7 +178,10 @@ async function fetchDefaultBranch(
177178
const res = await fetch(url)
178179
if (!res.ok) return { branch: 'main', exists: false }
179180
const data = await res.json()
180-
return { branch: data.default_branch || 'main', exists: true }
181+
// The response is remote data, so it is a taint source in its own right --
182+
// it flows straight back into the tree and contents URLs. Run it through
183+
// the same ref check as user input and fall back to 'main' if it fails.
184+
return { branch: sanitizeBranch(String(data.default_branch ?? '')) || 'main', exists: true }
181185
} catch {
182186
return { branch: 'main', exists: false }
183187
}

0 commit comments

Comments
 (0)