Merge commit from fork

A dependency graph file downloaded from another workflow run may come from an
untrusted fork. Passing the parsed file whole to octokit.request() let keys such
as 'url' override the request destination and leak the token.
This commit is contained in:
Jérôme Prinet authored and GitHub committed 2026-10-28 13:28:01 +01:00
1 parent bf74269609
commit dcf6f72a92
2 files changed
+29 -4

No files matched your search

+18 -4
View File
@@ -218,14 +218,28 @@ async function submitDependencyGraphFile(jsonFile: string): Promise<void> {
const octokit = getOctokit()
const jsonContent = fs.readFileSync(jsonFile, 'utf8')
const jsonObject = JSON.parse(jsonContent)
jsonObject.owner = github.context.repo.owner
jsonObject.repo = github.context.repo.repo
const response = await octokit.request('POST /repos/{owner}/{repo}/dependency-graph/snapshots', jsonObject)
const snapshot = toSnapshotRequest(JSON.parse(jsonContent), github.context.repo.owner, github.context.repo.repo)
const response = await octokit.request('POST /repos/{owner}/{repo}/dependency-graph/snapshots', snapshot)
const relativeJsonFile = getRelativePathFromWorkspace(jsonFile)
core.notice(`Submitted ${relativeJsonFile}: ${response.data.message}`)
}
// Only the snapshot fields are copied into the request: the file may come from an untrusted artifact,
// and any other key (such as 'url', 'baseUrl' or 'headers') would be interpreted as a request option.
const SNAPSHOT_FIELDS = ['version', 'job', 'sha', 'ref', 'detector', 'metadata', 'manifests', 'scanned'] as const
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function toSnapshotRequest(jsonObject: Record<string, unknown>, owner: string, repo: string): any {
const snapshot: Record<string, unknown> = {owner, repo}
for (const field of SNAPSHOT_FIELDS) {
if (jsonObject[field] !== undefined) {
snapshot[field] = jsonObject[field]
}
}
return snapshot
}
function getReportDirectory(): string {
return process.env.DEPENDENCY_GRAPH_REPORT_DIR!
}
@@ -1,6 +1,7 @@
import {describe, expect, it} from '@jest/globals'
import {DependencyGraphConfig} from "../../src/configuration"
import {toSnapshotRequest} from "../../src/dependency-graph"
describe('dependency-graph', () => {
describe('constructs job correlator', () => {
@@ -33,4 +34,14 @@ describe('dependency-graph', () => {
expect(id).toBe('workflow-jobid-windows-211-value_with_comma')
})
})
describe('constructs snapshot request', () => {
it('copies snapshot fields and sets owner and repo', () => {
const snapshot = {version: 0, job: {correlator: 'c', id: '1'}, sha: 'abc', ref: 'refs/heads/main', detector: {name: 'd'}, scanned: '2026-10-27T00:00:00Z', manifests: {}}
expect(toSnapshotRequest(snapshot, 'owner', 'repo')).toEqual({owner: 'owner', repo: 'repo', ...snapshot})
})
it('drops request options supplied by the file', () => {
const request = toSnapshotRequest({version: 0, url: 'https://attacker.example', baseUrl: 'https://attacker.example', headers: {}, method: 'GET', request: {}, owner: 'other', repo: 'other'}, 'owner', 'repo')
expect(request).toEqual({owner: 'owner', repo: 'repo', version: 0})
})
})
})