Skip to content

Commit e96ae3d

Browse files
committed
feat: populate metadata.component.externalReferences VCS and build-system from common CI environment variables
resolves #1344 Signed-off-by: Jeremy Long <jeremy.long@gmail.com>
1 parent 4ee0973 commit e96ae3d

File tree

2 files changed

+107
-3
lines changed

2 files changed

+107
-3
lines changed

src/_helpers.ts

Lines changed: 79 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ SPDX-License-Identifier: Apache-2.0
1717
Copyright (c) OWASP Foundation. All Rights Reserved.
1818
*/
1919

20+
import { execSync } from 'child_process'
2021
import { existsSync, readFileSync } from 'fs'
2122
import { dirname, extname, isAbsolute, join, parse, sep } from 'path'
2223

@@ -126,7 +127,84 @@ export function getMimeForLicenseFile (filename: string): MimeType | undefined {
126127
const { name, ext } = parse(filename.toLowerCase())
127128
return LICENSE_FILENAME_BASE.has(name) && LICENSE_FILENAME_EXT.has(ext)
128129
? MIME_TEXT_PLAIN
129-
: MAP_TEXT_EXTENSION_MIME[ext]
130+
: MAP_TEXT_EXTENSION_MIME[ext] ?? undefined
130131
}
131132

132133
// endregion MIME
134+
135+
export function detectBuildUrl (): string | undefined {
136+
if (process.env.GITHUB_ACTIONS === 'true') {
137+
return `${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}`
138+
}
139+
if (process.env.GITLAB_CI === 'true' && isNonNullable(process.env.CI_JOB_URL)) {
140+
return process.env.CI_JOB_URL
141+
}
142+
if (isNonNullable(process.env.CIRCLECI)) {
143+
return process.env.CIRCLE_BUILD_URL
144+
}
145+
if (isNonNullable(process.env.JENKINS_URL)) {
146+
return process.env.BUILD_URL
147+
}
148+
if (isNonNullable(process.env.TF_BUILD)) {
149+
return `${process.env.SYSTEM_TEAMFOUNDATIONCOLLECTIONURI}${process.env.SYSTEM_TEAMPROJECT}/_build/results?buildId=${process.env.BUILD_BUILDID}`
150+
}
151+
if (isNonNullable(process.env.TRAVIS)) {
152+
return process.env.TRAVIS_BUILD_WEB_URL
153+
}
154+
if (isNonNullable(process.env.BITBUCKET_BUILD_NUMBER)) {
155+
return process.env.BITBUCKET_GIT_HTTP_ORIGIN
156+
}
157+
if (isNonNullable(process.env.CODEBUILD_PUBLIC_BUILD_URL)) {
158+
return process.env.CODEBUILD_PUBLIC_BUILD_URL
159+
}
160+
if (isNonNullable(process.env.DRONE_BUILD_LINK)) {
161+
return process.env.DRONE_BUILD_LINK
162+
}
163+
return undefined
164+
}
165+
166+
export function detectSourceUrl(): string | undefined {
167+
try {
168+
const hasGit = execSync('which git', { stdio: 'ignore' })
169+
if (hasGit !== null && hasGit.length > 0) {
170+
const gitUrl = execSync('git remote get-url origin 2>/dev/null', { encoding: 'utf8' }).trim()
171+
if (gitUrl !== null && gitUrl !== '') {
172+
if (gitUrl.startsWith('git@') && gitUrl.endsWith('.git')) {
173+
return gitUrl.replace(':', '/').replace('git@', 'https://')
174+
}
175+
return gitUrl
176+
}
177+
}
178+
} catch (error) {
179+
// Fall through to environment checks if git commands fail
180+
}
181+
182+
if (isNonNullable(process.env.GITHUB_REPOSITORY)) {
183+
return `${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}`
184+
}
185+
if (isNonNullable(process.env.GITLAB_CI)) {
186+
return process.env.CI_REPOSITORY_URL
187+
}
188+
if (isNonNullable(process.env.CIRCLECI)) {
189+
return process.env.CIRCLE_REPOSITORY_URL
190+
}
191+
if (isNonNullable(process.env.JENKINS_URL)) {
192+
return process.env.GIT_URL
193+
}
194+
if (isNonNullable(process.env.TF_BUILD)) {
195+
return process.env.BUILD_REPOSITORY_URI
196+
}
197+
if (isNonNullable(process.env.BITBUCKET_GIT_HTTP_ORIGIN)) {
198+
return process.env.BITBUCKET_GIT_HTTP_ORIGIN
199+
}
200+
if (isNonNullable(process.env.BITBUCKET_GIT_SSH_ORIGIN)) {
201+
return process.env.BITBUCKET_GIT_SSH_ORIGIN
202+
}
203+
if (isNonNullable(process.env.CODEBUILD_BUILD_ID)) {
204+
return process.env.CODEBUILD_SOURCE_REPO_URL
205+
}
206+
if (isNonNullable(process.env.DRONE_REPO_LINK)) {
207+
return process.env.DRONE_REPO_LINK
208+
}
209+
return undefined
210+
}

src/plugin.ts

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -316,9 +316,35 @@ export class CycloneDxWebpackPlugin {
316316
: { name: this.rootComponentName, version: this.rootComponentVersion }
317317
if (thisPackageJson === undefined) { return undefined }
318318
normalizePackageJson(thisPackageJson, w => { logger.debug('normalizePackageJson from PkgPath', path, 'caused:', w) })
319-
return builder.makeComponent(thisPackageJson)
320-
}
319+
// return builder.makeComponent(thisPackageJson)
320+
const component = builder.makeComponent(thisPackageJson)
321+
322+
if (component !== undefined) {
323+
const sourceUrl = (this as any).sourceUrl
324+
if (sourceUrl !== undefined) {
325+
component.externalReferences.add({
326+
type: CDX.Enums.ExternalReferenceType.VCS,
327+
url: sourceUrl,
328+
hashes: new CDX.Models.HashDictionary(),
329+
comment: '',
330+
compare: () => 0
331+
})
332+
}
333+
334+
const buildUrl = (this as any).buildUrl
335+
if (buildUrl !== undefined) {
336+
component.externalReferences.add({
337+
type: CDX.Enums.ExternalReferenceType.BuildSystem,
338+
url: buildUrl,
339+
hashes: new CDX.Models.HashDictionary(),
340+
comment: '',
341+
compare: () => 0
342+
})
343+
}
344+
}
321345

346+
return component
347+
}
322348
#finalizeBom (
323349
bom: CDX.Models.Bom,
324350
cdxToolBuilder: CDX.Builders.FromNodePackageJson.ToolBuilder,

0 commit comments

Comments
 (0)