diff --git a/src/providers/marker_evaluator.js b/src/providers/marker_evaluator.js new file mode 100644 index 00000000..b06755eb --- /dev/null +++ b/src/providers/marker_evaluator.js @@ -0,0 +1,192 @@ +// PEP 508 environment marker evaluator. +// Filters Python dependencies by platform/version markers so that e.g. +// "pywin32 ; sys_platform == 'win32'" is excluded on Linux/macOS. +// See https://peps.python.org/pep-0508/#environment-markers + +import os from 'node:os' + +import { getCustomPath, invokeCommand } from '../tools.js' + +let cachedPythonVersions = undefined + +function getPythonVersions() { + if (cachedPythonVersions !== undefined) { return cachedPythonVersions } + try { + let python = getCustomPath('python3') + let out = invokeCommand(python, ['-c', "import sys; v=sys.version_info; print(f'{v.major}.{v.minor} {v.major}.{v.minor}.{v.micro}')"], + { timeout: 5000 }).toString().trim() + let [short, full] = out.split(' ') + cachedPythonVersions = { short, full } + } catch { + cachedPythonVersions = null + } + return cachedPythonVersions +} + +/** + * Maps Node.js/OS values to PEP 508 marker variables. + * Example: on Linux, sys_platform='linux', platform_system='Linux', os_name='posix' + * @returns {Record} + */ +export function getEnvironmentMarkers() { + let platform = process.platform + let systemMap = { win32: 'Windows', linux: 'Linux', darwin: 'Darwin' } + let machine = typeof os.machine === 'function' ? os.machine() : process.arch + let pyVer = getPythonVersions() + return { + sys_platform: platform, + platform_system: systemMap[platform] || platform, + os_name: platform === 'win32' ? 'nt' : 'posix', + platform_machine: machine, + platform_release: os.release(), + platform_version: os.version?.() || '', + python_version: pyVer?.short || '', + python_full_version: pyVer?.full || '', + implementation_name: 'cpython', + } +} + +function compareVersions(left, right) { + let lParts = left.split('.').map(Number) + let rParts = right.split('.').map(Number) + let len = Math.max(lParts.length, rParts.length) + for (let i = 0; i < len; i++) { + let l = lParts[i] || 0 + let r = rParts[i] || 0 + if (l < r) { return -1 } + if (l > r) { return 1 } + } + return 0 +} + +// Evaluates a single comparison like sys_platform == 'win32' or python_version >= '3.8'. +// Version-bearing variables (python_version, python_full_version) use numeric comparison; +// all others use string equality. Returns false when the env value is missing. +function evaluateComparison(variable, op, value, env) { + let envVal = env[variable] + if (envVal === undefined || envVal === '') { + return false + } + + let isVersion = variable.includes('version') + if (isVersion) { + let cmp = compareVersions(envVal, value) + switch (op) { + case '==': return cmp === 0 + case '!=': return cmp !== 0 + case '>=': return cmp >= 0 + case '<=': return cmp <= 0 + case '>': return cmp > 0 + case '<': return cmp < 0 + case '~=': { + let parts = value.split('.') + parts.pop() + let prefix = parts.join('.') + return envVal.startsWith(prefix) && cmp >= 0 + } + default: return true + } + } + + switch (op) { + case '==': return envVal === value + case '!=': return envVal !== value + case 'in': return value.includes(envVal) + case 'not in': return !value.includes(envVal) + default: return envVal === value + } +} + +// Parses a single marker comparison into {variable, op, value}. +// Handles both normal and reversed forms: +// "sys_platform == 'linux'" → { variable: 'sys_platform', op: '==', value: 'linux' } +// "'linux' == sys_platform" → { variable: 'sys_platform', op: '==', value: 'linux' } +function parseAtom(expr) { + // Normal form: variable op 'value' + let m = expr.match(/^\s*([\w.]+)\s*(~=|!=|==|>=|<=|>|<|not\s+in|in)\s*["']([^"']*)["']\s*$/) + if (m) { return { variable: m[1], op: m[2].replace(/\s+/g, ' '), value: m[3] } } + + // Reversed form: 'value' op variable — reverse directional operators + let mReverse = expr.match(/^\s*["']([^"']*)['"]\s*(~=|!=|==|>=|<=|>|<|not\s+in|in)\s*([\w.]+)\s*$/) + if (mReverse) { + let reverseOp = { '<': '>', '>': '<', '<=': '>=', '>=': '<=' } + let op = mReverse[2].replace(/\s+/g, ' ') + return { variable: mReverse[3], op: reverseOp[op] || op, value: mReverse[1] } + } + + return null +} + +/** + * Evaluates a full PEP 508 marker expression against the current platform. + * Example: "sys_platform == 'win32' and python_version >= '3.8'" → false on Linux + * Empty/missing markers return true (unconditional dependency). + * @param {string} markerExpr + * @returns {boolean} + */ +export function evaluateMarker(markerExpr) { + if (!markerExpr || !markerExpr.trim()) { return true } + let env = getEnvironmentMarkers() + return evaluateExpr(markerExpr.trim(), env) +} + +function evaluateExpr(expr, env) { + let orParts = splitLogical(expr, ' or ') + if (orParts.length > 1) { + return orParts.some(part => evaluateExpr(part, env)) + } + + let andParts = splitLogical(expr, ' and ') + if (andParts.length > 1) { + return andParts.every(part => evaluateExpr(part, env)) + } + + let trimmed = expr.trim() + if (trimmed.startsWith('(') && trimmed.endsWith(')')) { + return evaluateExpr(trimmed.slice(1, -1), env) + } + + let atom = parseAtom(trimmed) + if (!atom) { return true } + + return evaluateComparison(atom.variable, atom.op, atom.value, env) +} + +// Splits an expression by " and " or " or " at the top level, skipping +// separators inside parentheses or quoted strings. +// Example: splitLogical("a == 'x' and (b == 'y' or c == 'z')", " and ") +// → ["a == 'x'", "(b == 'y' or c == 'z')"] +function splitLogical(expr, sep) { + let parts = [] + let depth = 0 + let current = '' + let i = 0 + let quoteChar = null + while (i < expr.length) { + let ch = expr[i] + if (quoteChar) { + if (ch === quoteChar) { quoteChar = null } + current += ch + i++ + continue + } + if (ch === '"' || ch === "'") { + quoteChar = ch + current += ch + i++ + continue + } + if (ch === '(') { depth++ } + if (ch === ')') { depth-- } + if (depth === 0 && expr.substring(i, i + sep.length) === sep) { + parts.push(current) + current = '' + i += sep.length + continue + } + current += ch + i++ + } + parts.push(current) + return parts.filter(p => p.trim()) +} diff --git a/src/providers/python_poetry.js b/src/providers/python_poetry.js index 41140ea2..0222f9c0 100644 --- a/src/providers/python_poetry.js +++ b/src/providers/python_poetry.js @@ -1,9 +1,12 @@ import fs from 'node:fs' import path from 'node:path' +import { parse as parseToml } from 'smol-toml' + import { environmentVariableIsPopulated, getCustom, getCustomPath, invokeCommand } from '../tools.js' import Base_pyproject from './base_pyproject.js' +import { evaluateMarker } from './marker_evaluator.js' export default class Python_poetry extends Base_pyproject { @@ -50,7 +53,9 @@ export default class Python_poetry extends Base_pyproject { let treeOutput = this._getPoetryShowTreeOutput(manifestDir, hasDevGroup, opts) let showAllOutput = this._getPoetryShowAllOutput(manifestDir, opts) let versionMap = this._parsePoetryShowAll(showAllOutput) - return this._parsePoetryTree(treeOutput, versionMap) + let lockDir = this._findLockFileDir(manifestDir, opts) + let markerData = this._extractMarkerData(lockDir, parsed) + return this._parsePoetryTree(treeOutput, versionMap, markerData) } /** @@ -107,16 +112,64 @@ export default class Python_poetry extends Base_pyproject { return versions } + /** + * Collects PEP 508 marker expressions for direct and transitive deps. + * Direct markers come from pyproject.toml dependency strings, e.g.: + * "pywin32>=311 ; sys_platform == 'win32'" → directMarkers['pywin32'] = "sys_platform == 'win32'" + * Transitive markers come from poetry.lock [package.dependencies] entries, e.g.: + * colorama = {version = "*", markers = "sys_platform == 'win32'"} + * → transitiveMarkers['click']['colorama'] = "sys_platform == 'win32'" + * @param {string|null} lockDir + * @param {object} parsed - parsed pyproject.toml + * @returns {{directMarkers: Map, transitiveMarkers: Map>}} + */ + _extractMarkerData(lockDir, parsed) { + let directMarkers = new Map() + let transitiveMarkers = new Map() + + // Extract markers from PEP 621 dependency strings: "name[extras]>=ver ; marker" + let deps = parsed.project?.dependencies || [] + for (let dep of deps) { + let m = dep.match(/^([A-Za-z0-9][A-Za-z0-9._-]*)\s*[^;]*;\s*(.+)$/) + if (m) { + directMarkers.set(this._canonicalize(m[1]), m[2].trim()) + } + } + + if (lockDir) { + let lockPath = path.join(lockDir, this._lockFileName()) + if (fs.existsSync(lockPath)) { + let lockContent = fs.readFileSync(lockPath, 'utf-8') + let lock = parseToml(lockContent) + let packages = lock.package || [] + for (let pkg of packages) { + let pkgKey = this._canonicalize(pkg.name) + let pkgDeps = pkg.dependencies || {} + for (let [depName, depSpec] of Object.entries(pkgDeps)) { + let markers = typeof depSpec === 'object' && depSpec != null ? depSpec.markers : null + if (markers) { + if (!transitiveMarkers.has(pkgKey)) { + transitiveMarkers.set(pkgKey, new Map()) + } + transitiveMarkers.get(pkgKey).set(this._canonicalize(depName), markers) + } + } + } + } + } + + return { directMarkers, transitiveMarkers } + } + /** * Parse poetry show --tree output into a dependency graph structure. - * Top-level lines (no indentation/tree chars) are direct deps: "name version description" - * Indented lines are transitive deps with tree chars: "├── name >=constraint" * * @param {string} treeOutput * @param {Map} versionMap - canonical name -> resolved version + * @param {{directMarkers: Map, transitiveMarkers: Map>}} markerData * @returns {{directDeps: string[], graph: Map}} */ - _parsePoetryTree(treeOutput, versionMap) { + _parsePoetryTree(treeOutput, versionMap, markerData) { let lines = treeOutput.split(/\r?\n/) let graph = new Map() let directDeps = [] @@ -133,6 +186,14 @@ export default class Python_poetry extends Base_pyproject { let name = topMatch[1] let version = topMatch[2] let key = this._canonicalize(name) + + let marker = markerData.directMarkers.get(key) + if (marker && !evaluateMarker(marker)) { + currentDirectDep = null + stack = [] + continue + } + directDeps.push(key) if (!graph.has(key)) { graph.set(key, { name, version, children: [] }) @@ -159,6 +220,22 @@ export default class Python_poetry extends Base_pyproject { let prefix = line.substring(0, nameStart) let depth = (prefix.match(/(?:[├└│ ][\s─]{2} ?)/g) || []).length + // pop stack back to find the parent at depth-1 + while (stack.length > 0 && stack[stack.length - 1].depth >= depth) { + stack.pop() + } + + let parentKey = stack.length > 0 ? stack[stack.length - 1].key : null + if (parentKey) { + let parentMarkers = markerData.transitiveMarkers.get(parentKey) + if (parentMarkers) { + let marker = parentMarkers.get(depKey) + if (marker && !evaluateMarker(marker)) { + continue + } + } + } + // resolve version from the version map let version = versionMap.get(depKey) || null if (!version) { @@ -169,13 +246,7 @@ export default class Python_poetry extends Base_pyproject { graph.set(depKey, { name: depName, version, children: [] }) } - // pop stack back to find the parent at depth-1 - while (stack.length > 0 && stack[stack.length - 1].depth >= depth) { - stack.pop() - } - - if (stack.length > 0) { - let parentKey = stack[stack.length - 1].key + if (parentKey) { let parentEntry = graph.get(parentKey) if (parentEntry && !parentEntry.children.includes(depKey)) { parentEntry.children.push(depKey) diff --git a/src/providers/python_uv.js b/src/providers/python_uv.js index aa021706..bce7c9cf 100644 --- a/src/providers/python_uv.js +++ b/src/providers/python_uv.js @@ -6,6 +6,7 @@ import { parse as parseToml } from 'smol-toml' import { environmentVariableIsPopulated, getCustomPath, invokeCommand } from '../tools.js' import Base_pyproject from './base_pyproject.js' +import { evaluateMarker } from './marker_evaluator.js' import { getParser, getPinnedVersionQuery } from './requirements_parser.js' export default class Python_uv extends Base_pyproject { @@ -98,6 +99,17 @@ export default class Python_uv extends Base_pyproject { let nameNode = child.children.find(c => c.type === 'package') if (!nameNode) { continue } + // Skip packages with non-matching PEP 508 markers, e.g. "pywin32==311 ; sys_platform == 'win32'" + let markerNode = child.children.find(c => c.type === 'marker_spec') + if (markerNode) { + let markerText = markerNode.text.replace(/^\s*;\s*/, '') + if (!evaluateMarker(markerText)) { + currentPkg = null + collectingVia = false + continue + } + } + let name = nameNode.text let version = null let versionMatches = pinnedVersionQuery.matches(child) diff --git a/test/providers/marker_evaluator.test.js b/test/providers/marker_evaluator.test.js new file mode 100644 index 00000000..5b718e9a --- /dev/null +++ b/test/providers/marker_evaluator.test.js @@ -0,0 +1,118 @@ +import { expect } from 'chai' + +import { evaluateMarker, getEnvironmentMarkers } from '../../src/providers/marker_evaluator.js' + +let originalPlatform + +suite('PEP 508 marker evaluator', () => { + suiteSetup(() => { + originalPlatform = process.platform + Object.defineProperty(process, 'platform', { value: 'linux', configurable: true }) + }) + suiteTeardown(() => { + Object.defineProperty(process, 'platform', { value: originalPlatform, configurable: true }) + }) + + suite('in operator — PEP 508 string containment', () => { + test('substring match: sys_platform in wider string', () => { + expect(evaluateMarker("sys_platform in 'linux2'")).to.be.true + }) + + test('exact match: sys_platform in exact string', () => { + expect(evaluateMarker("sys_platform in 'linux'")).to.be.true + }) + + test('no match: sys_platform in unrelated string', () => { + expect(evaluateMarker("sys_platform in 'win32'")).to.be.false + }) + + test('partial overlap but no containment', () => { + expect(evaluateMarker("sys_platform in 'lin'")).to.be.false + }) + }) + + suite('not in operator — negated string containment', () => { + test('substring present: sys_platform not in wider string', () => { + expect(evaluateMarker("sys_platform not in 'linux2'")).to.be.false + }) + + test('no match: sys_platform not in unrelated string', () => { + expect(evaluateMarker("sys_platform not in 'win32'")).to.be.true + }) + + test('partial overlap negated', () => { + expect(evaluateMarker("sys_platform not in 'lin'")).to.be.true + }) + }) + + suite('reversed form — directional operator reversal', () => { + test('>= is reversed to <= when operands are swapped', () => { + expect(evaluateMarker("'3.8' >= python_version")).to.equal( + evaluateMarker("python_version <= '3.8'") + ) + }) + + test('< is reversed to > when operands are swapped', () => { + expect(evaluateMarker("'3.8' < python_version")).to.equal( + evaluateMarker("python_version > '3.8'") + ) + }) + + test('> is reversed to < when operands are swapped', () => { + expect(evaluateMarker("'3.8' > python_version")).to.equal( + evaluateMarker("python_version < '3.8'") + ) + }) + + test('<= is reversed to >= when operands are swapped', () => { + expect(evaluateMarker("'3.8' <= python_version")).to.equal( + evaluateMarker("python_version >= '3.8'") + ) + }) + + test('== is unchanged when operands are swapped', () => { + expect(evaluateMarker("'linux' == sys_platform")).to.equal( + evaluateMarker("sys_platform == 'linux'") + ) + }) + + test('!= is unchanged when operands are swapped', () => { + expect(evaluateMarker("'win32' != sys_platform")).to.equal( + evaluateMarker("sys_platform != 'win32'") + ) + }) + }) + + suite('python_version vs python_full_version', () => { + test('python_version matches X.Y format', () => { + let env = getEnvironmentMarkers() + if (env.python_version) { + expect(env.python_version).to.match(/^\d+\.\d+$/) + } + }) + + test('python_full_version matches X.Y.Z format', () => { + let env = getEnvironmentMarkers() + if (env.python_full_version) { + expect(env.python_full_version).to.match(/^\d+\.\d+\.\d+$/) + } + }) + + test('python_full_version starts with python_version', () => { + let env = getEnvironmentMarkers() + if (env.python_version && env.python_full_version) { + expect(env.python_full_version).to.satisfy( + v => v.startsWith(env.python_version + '.') + ) + } + }) + + test('python_full_version marker evaluates with micro version', () => { + let env = getEnvironmentMarkers() + if (env.python_full_version) { + expect(evaluateMarker(`python_full_version >= '${env.python_full_version}'`)).to.be.true + expect(evaluateMarker(`python_full_version == '${env.python_full_version}'`)).to.be.true + } + }) + }) +}) diff --git a/test/providers/python_pyproject.test.js b/test/providers/python_pyproject.test.js index 9eac98fb..a3e8bd43 100644 --- a/test/providers/python_pyproject.test.js +++ b/test/providers/python_pyproject.test.js @@ -9,6 +9,7 @@ import Python_poetry from '../../src/providers/python_poetry.js' import Python_uv from '../../src/providers/python_uv.js' let clock +let originalPlatform const TIMEOUT = process.env.GITHUB_ACTIONS ? 30000 : 15000 @@ -343,6 +344,171 @@ suite('testing the python-pyproject data provider', () => { } }) + suite('uv projects - PEP 508 marker filtering (TC-4042 reproducer)', () => { + const fixtureDir = `${MANIFESTS}/uv_marker_filtering` + + function makeUvExport(markerLines) { + let lines = [ + 'click==8.3.1', + ' # via marker-test', + 'six==1.16.0', + ' # via marker-test', + ] + for (let ml of markerLines) { + lines.push(ml.line) + lines.push(` # via ${ml.via || 'click'}`) + } + return lines.join('\n') + } + + function injectAndRun(uvExportContent, fn) { + return async () => { + process.env['TRUSTIFY_DA_UV_EXPORT'] = Buffer.from(uvExportContent).toString('base64') + try { await fn() } finally { delete process.env['TRUSTIFY_DA_UV_EXPORT'] } + } + } + + const EXCLUDED_MARKERS = [ + {desc: 'sys_platform == win32', line: "colorama==0.4.6 ; sys_platform == 'win32'"}, + {desc: 'platform_system == Windows', line: "colorama==0.4.6 ; platform_system == 'Windows'"}, + {desc: 'os_name == nt', line: "colorama==0.4.6 ; os_name == 'nt'"}, + {desc: 'compound marker (win32 and python)', line: "colorama==0.4.6 ; sys_platform == 'win32' and python_version >= '3.8'"}, + ] + + EXCLUDED_MARKERS.forEach(({desc, line}) => { + test(`packages with non-matching marker excluded from stack: ${desc}`, injectAndRun( + makeUvExport([{line}]), + async () => { + let result = await uvProvider.provideStack(path.join(fixtureDir, 'pyproject.toml')) + let sbom = JSON.parse(result.content) + let names = sbom.components.map(c => c.name) + expect(names).to.include('click') + expect(names).to.not.include('colorama', + `colorama with marker "${desc}" should be excluded on this platform`) + } + )).timeout(TIMEOUT) + }) + + const INCLUDED_MARKERS = [ + {desc: 'python_version >= 3.8 (matches most systems)', line: "six==1.16.0 ; python_version >= '3.8'", pkg: 'six', via: 'marker-test'}, + {desc: 'os_name == posix (matches linux/macOS)', line: "six==1.16.0 ; os_name == 'posix'", pkg: 'six', via: 'marker-test'}, + ] + + INCLUDED_MARKERS.forEach(({desc, line, pkg, via}) => { + test(`packages with matching marker included in stack: ${desc}`, injectAndRun( + makeUvExport([{line, via}]), + async () => { + let result = await uvProvider.provideStack(path.join(fixtureDir, 'pyproject.toml')) + let sbom = JSON.parse(result.content) + let names = sbom.components.map(c => c.name) + expect(names).to.include(pkg, + `${pkg} with marker "${desc}" should be included on this platform`) + } + )).timeout(TIMEOUT) + }) + + test('click dependency tree should not reference colorama when marker does not match', injectAndRun( + makeUvExport([{line: "colorama==0.4.6 ; sys_platform == 'win32'"}]), + async () => { + let result = await uvProvider.provideStack(path.join(fixtureDir, 'pyproject.toml')) + let sbom = JSON.parse(result.content) + let clickDep = sbom.dependencies.find(d => d.ref.includes('/click@')) + expect(clickDep).to.exist + expect(clickDep.dependsOn).to.deep.equal([], + 'click should have no children since colorama is marker-filtered') + } + )).timeout(TIMEOUT) + }) + + suite('poetry projects - PEP 508 marker filtering (TC-4042 reproducer)', () => { + const fixtureDir = `${MANIFESTS}/poetry_marker_filtering` + + function injectPoetryAndRun(treeLines, allLines, fn) { + return async () => { + process.env['TRUSTIFY_DA_POETRY_SHOW_TREE'] = Buffer.from(treeLines.join('\n')).toString('base64') + process.env['TRUSTIFY_DA_POETRY_SHOW_ALL'] = Buffer.from(allLines.join('\n')).toString('base64') + try { await fn() } finally { + delete process.env['TRUSTIFY_DA_POETRY_SHOW_TREE'] + delete process.env['TRUSTIFY_DA_POETRY_SHOW_ALL'] + } + } + } + + const showAll = [ + 'click 8.3.1 Composable command line interface toolkit', + 'colorama 0.4.6 Cross-platform colored terminal text.', + 'six 1.16.0 Python 2 and 3 compatibility utilities', + 'win32-setctime 1.1.0 A small Python utility to set file creation time on Windows', + 'pyreadline3 3.4.1 A Python implementation of GNU readline.', + ] + + const EXCLUDED_CASES = [ + { + desc: 'platform_system == Windows (colorama via click)', + tree: [ + 'click 8.3.1 Composable command line interface toolkit', + '└── colorama *', + 'six 1.16.0 Python 2 and 3 compatibility utilities', + ], + excluded: 'colorama', + included: ['click', 'six'], + }, + { + desc: 'os_name == nt (win32-setctime as direct dep)', + tree: [ + 'click 8.3.1 Composable command line interface toolkit', + 'six 1.16.0 Python 2 and 3 compatibility utilities', + 'win32-setctime 1.1.0 A small Python utility to set file creation time on Windows', + ], + excluded: 'win32-setctime', + included: ['click', 'six'], + }, + { + desc: 'sys_platform == win32 (pyreadline3 as direct dep)', + tree: [ + 'click 8.3.1 Composable command line interface toolkit', + 'six 1.16.0 Python 2 and 3 compatibility utilities', + 'pyreadline3 3.4.1 A Python implementation of GNU readline.', + ], + excluded: 'pyreadline3', + included: ['click', 'six'], + }, + ] + + EXCLUDED_CASES.forEach(({desc, tree, excluded, included}) => { + test(`packages with non-matching marker excluded from stack: ${desc}`, injectPoetryAndRun( + tree, showAll, + async () => { + let result = await poetryProvider.provideStack(path.join(fixtureDir, 'pyproject.toml')) + let sbom = JSON.parse(result.content) + let names = sbom.components.map(c => c.name) + for (let inc of included) { + expect(names).to.include(inc) + } + expect(names).to.not.include(excluded, + `${excluded} with marker "${desc}" should be excluded on this platform`) + } + )).timeout(TIMEOUT) + }) + + test('click dependency tree should not reference colorama when marker does not match', injectPoetryAndRun( + [ + 'click 8.3.1 Composable command line interface toolkit', + '└── colorama *', + 'six 1.16.0 Python 2 and 3 compatibility utilities', + ], + showAll, + async () => { + let result = await poetryProvider.provideStack(path.join(fixtureDir, 'pyproject.toml')) + let sbom = JSON.parse(result.content) + let clickDep = sbom.dependencies.find(d => d.ref.includes('/click@')) + expect(clickDep).to.exist + expect(clickDep.dependsOn).to.deep.equal([], + 'click should have no children since colorama is marker-filtered') + } + )).timeout(TIMEOUT) + }) + suite('workspace/monorepo support', () => { const uvWorkspace = `${MANIFESTS}/uv_workspace` @@ -425,4 +591,11 @@ suite('testing the python-pyproject data provider', () => { }) }) -}).beforeAll(() => clock = useFakeTimers(new Date('2023-10-01T00:00:00.000Z'))).afterAll(() => clock.restore()) +}).beforeAll(() => { + originalPlatform = process.platform + Object.defineProperty(process, 'platform', { value: 'linux', configurable: true }) + clock = useFakeTimers(new Date('2023-10-01T00:00:00.000Z')) +}).afterAll(() => { + Object.defineProperty(process, 'platform', { value: originalPlatform, configurable: true }) + clock.restore() +}) diff --git a/test/providers/tst_manifests/pyproject/pep621_ignore_and_extras/expected_component_sbom.json b/test/providers/tst_manifests/pyproject/pep621_ignore_and_extras/expected_component_sbom.json index 7d1936f7..3ddae63d 100644 --- a/test/providers/tst_manifests/pyproject/pep621_ignore_and_extras/expected_component_sbom.json +++ b/test/providers/tst_manifests/pyproject/pep621_ignore_and_extras/expected_component_sbom.json @@ -1,72 +1,60 @@ { - "bomFormat": "CycloneDX", - "specVersion": "1.4", - "version": 1, - "metadata": { - "timestamp": "2023-10-01T00:00:00.000Z", - "component": { - "name": "test-project", - "version": "1.0.0", - "purl": "pkg:pypi/test-project@1.0.0", - "type": "application", - "bom-ref": "pkg:pypi/test-project@1.0.0" - } - }, - "components": [ - { - "name": "flask", - "version": "2.0.3", - "purl": "pkg:pypi/flask@2.0.3", - "type": "library", - "bom-ref": "pkg:pypi/flask@2.0.3" - }, - { - "name": "pywin32", - "version": "311", - "purl": "pkg:pypi/pywin32@311", - "type": "library", - "bom-ref": "pkg:pypi/pywin32@311" - }, - { - "name": "requests", - "version": "2.25.1", - "purl": "pkg:pypi/requests@2.25.1", - "type": "library", - "bom-ref": "pkg:pypi/requests@2.25.1" - }, - { - "name": "typing-extensions", - "version": "4.1.1", - "purl": "pkg:pypi/typing-extensions@4.1.1", - "type": "library", - "bom-ref": "pkg:pypi/typing-extensions@4.1.1" - } - ], - "dependencies": [ - { - "ref": "pkg:pypi/test-project@1.0.0", - "dependsOn": [ - "pkg:pypi/flask@2.0.3", - "pkg:pypi/pywin32@311", - "pkg:pypi/requests@2.25.1", - "pkg:pypi/typing-extensions@4.1.1" - ] - }, - { - "ref": "pkg:pypi/flask@2.0.3", - "dependsOn": [] - }, - { - "ref": "pkg:pypi/pywin32@311", - "dependsOn": [] - }, - { - "ref": "pkg:pypi/requests@2.25.1", - "dependsOn": [] - }, - { - "ref": "pkg:pypi/typing-extensions@4.1.1", - "dependsOn": [] - } - ] + "bomFormat": "CycloneDX", + "specVersion": "1.4", + "version": 1, + "metadata": { + "timestamp": "2023-10-01T00:00:00.000Z", + "component": { + "name": "test-project", + "version": "1.0.0", + "purl": "pkg:pypi/test-project@1.0.0", + "type": "application", + "bom-ref": "pkg:pypi/test-project@1.0.0" + } + }, + "components": [ + { + "name": "flask", + "version": "2.0.3", + "purl": "pkg:pypi/flask@2.0.3", + "type": "library", + "bom-ref": "pkg:pypi/flask@2.0.3" + }, + { + "name": "requests", + "version": "2.25.1", + "purl": "pkg:pypi/requests@2.25.1", + "type": "library", + "bom-ref": "pkg:pypi/requests@2.25.1" + }, + { + "name": "typing-extensions", + "version": "4.1.1", + "purl": "pkg:pypi/typing-extensions@4.1.1", + "type": "library", + "bom-ref": "pkg:pypi/typing-extensions@4.1.1" + } + ], + "dependencies": [ + { + "ref": "pkg:pypi/test-project@1.0.0", + "dependsOn": [ + "pkg:pypi/flask@2.0.3", + "pkg:pypi/requests@2.25.1", + "pkg:pypi/typing-extensions@4.1.1" + ] + }, + { + "ref": "pkg:pypi/flask@2.0.3", + "dependsOn": [] + }, + { + "ref": "pkg:pypi/requests@2.25.1", + "dependsOn": [] + }, + { + "ref": "pkg:pypi/typing-extensions@4.1.1", + "dependsOn": [] + } + ] } diff --git a/test/providers/tst_manifests/pyproject/pep621_ignore_and_extras/expected_stack_sbom.json b/test/providers/tst_manifests/pyproject/pep621_ignore_and_extras/expected_stack_sbom.json index a89d1db8..f53d47df 100644 --- a/test/providers/tst_manifests/pyproject/pep621_ignore_and_extras/expected_stack_sbom.json +++ b/test/providers/tst_manifests/pyproject/pep621_ignore_and_extras/expected_stack_sbom.json @@ -1,183 +1,158 @@ { - "bomFormat": "CycloneDX", - "specVersion": "1.4", - "version": 1, - "metadata": { - "timestamp": "2023-10-01T00:00:00.000Z", - "component": { - "name": "test-project", - "version": "1.0.0", - "purl": "pkg:pypi/test-project@1.0.0", - "type": "application", - "bom-ref": "pkg:pypi/test-project@1.0.0" - } - }, - "components": [ - { - "name": "flask", - "version": "2.0.3", - "purl": "pkg:pypi/flask@2.0.3", - "type": "library", - "bom-ref": "pkg:pypi/flask@2.0.3" - }, - { - "name": "pywin32", - "version": "311", - "purl": "pkg:pypi/pywin32@311", - "type": "library", - "bom-ref": "pkg:pypi/pywin32@311" - }, - { - "name": "requests", - "version": "2.25.1", - "purl": "pkg:pypi/requests@2.25.1", - "type": "library", - "bom-ref": "pkg:pypi/requests@2.25.1" - }, - { - "name": "typing-extensions", - "version": "4.1.1", - "purl": "pkg:pypi/typing-extensions@4.1.1", - "type": "library", - "bom-ref": "pkg:pypi/typing-extensions@4.1.1" - }, - { - "name": "click", - "version": "8.3.1", - "purl": "pkg:pypi/click@8.3.1", - "type": "library", - "bom-ref": "pkg:pypi/click@8.3.1" - }, - { - "name": "colorama", - "version": "0.4.6", - "purl": "pkg:pypi/colorama@0.4.6", - "type": "library", - "bom-ref": "pkg:pypi/colorama@0.4.6" - }, - { - "name": "itsdangerous", - "version": "2.0.1", - "purl": "pkg:pypi/itsdangerous@2.0.1", - "type": "library", - "bom-ref": "pkg:pypi/itsdangerous@2.0.1" - }, - { - "name": "jinja2", - "version": "3.0.3", - "purl": "pkg:pypi/jinja2@3.0.3", - "type": "library", - "bom-ref": "pkg:pypi/jinja2@3.0.3" - }, - { - "name": "werkzeug", - "version": "2.0.3", - "purl": "pkg:pypi/werkzeug@2.0.3", - "type": "library", - "bom-ref": "pkg:pypi/werkzeug@2.0.3" - }, - { - "name": "certifi", - "version": "2023.7.22", - "purl": "pkg:pypi/certifi@2023.7.22", - "type": "library", - "bom-ref": "pkg:pypi/certifi@2023.7.22" - }, - { - "name": "chardet", - "version": "4.0.0", - "purl": "pkg:pypi/chardet@4.0.0", - "type": "library", - "bom-ref": "pkg:pypi/chardet@4.0.0" - }, - { - "name": "idna", - "version": "2.10", - "purl": "pkg:pypi/idna@2.10", - "type": "library", - "bom-ref": "pkg:pypi/idna@2.10" - }, - { - "name": "urllib3", - "version": "1.26.16", - "purl": "pkg:pypi/urllib3@1.26.16", - "type": "library", - "bom-ref": "pkg:pypi/urllib3@1.26.16" - } - ], - "dependencies": [ - { - "ref": "pkg:pypi/test-project@1.0.0", - "dependsOn": [ - "pkg:pypi/flask@2.0.3", - "pkg:pypi/pywin32@311", - "pkg:pypi/requests@2.25.1", - "pkg:pypi/typing-extensions@4.1.1" - ] - }, - { - "ref": "pkg:pypi/flask@2.0.3", - "dependsOn": [ - "pkg:pypi/click@8.3.1", - "pkg:pypi/itsdangerous@2.0.1", - "pkg:pypi/jinja2@3.0.3", - "pkg:pypi/werkzeug@2.0.3" - ] - }, - { - "ref": "pkg:pypi/pywin32@311", - "dependsOn": [] - }, - { - "ref": "pkg:pypi/requests@2.25.1", - "dependsOn": [ - "pkg:pypi/certifi@2023.7.22", - "pkg:pypi/chardet@4.0.0", - "pkg:pypi/idna@2.10", - "pkg:pypi/urllib3@1.26.16" - ] - }, - { - "ref": "pkg:pypi/typing-extensions@4.1.1", - "dependsOn": [] - }, - { - "ref": "pkg:pypi/click@8.3.1", - "dependsOn": [ - "pkg:pypi/colorama@0.4.6" - ] - }, - { - "ref": "pkg:pypi/colorama@0.4.6", - "dependsOn": [] - }, - { - "ref": "pkg:pypi/itsdangerous@2.0.1", - "dependsOn": [] - }, - { - "ref": "pkg:pypi/jinja2@3.0.3", - "dependsOn": [] - }, - { - "ref": "pkg:pypi/werkzeug@2.0.3", - "dependsOn": [] - }, - { - "ref": "pkg:pypi/certifi@2023.7.22", - "dependsOn": [] - }, - { - "ref": "pkg:pypi/chardet@4.0.0", - "dependsOn": [] - }, - { - "ref": "pkg:pypi/idna@2.10", - "dependsOn": [] - }, - { - "ref": "pkg:pypi/urllib3@1.26.16", - "dependsOn": [] - } - ] + "bomFormat": "CycloneDX", + "specVersion": "1.4", + "version": 1, + "metadata": { + "timestamp": "2023-10-01T00:00:00.000Z", + "component": { + "name": "test-project", + "version": "1.0.0", + "purl": "pkg:pypi/test-project@1.0.0", + "type": "application", + "bom-ref": "pkg:pypi/test-project@1.0.0" + } + }, + "components": [ + { + "name": "flask", + "version": "2.0.3", + "purl": "pkg:pypi/flask@2.0.3", + "type": "library", + "bom-ref": "pkg:pypi/flask@2.0.3" + }, + { + "name": "requests", + "version": "2.25.1", + "purl": "pkg:pypi/requests@2.25.1", + "type": "library", + "bom-ref": "pkg:pypi/requests@2.25.1" + }, + { + "name": "typing-extensions", + "version": "4.1.1", + "purl": "pkg:pypi/typing-extensions@4.1.1", + "type": "library", + "bom-ref": "pkg:pypi/typing-extensions@4.1.1" + }, + { + "name": "click", + "version": "8.3.1", + "purl": "pkg:pypi/click@8.3.1", + "type": "library", + "bom-ref": "pkg:pypi/click@8.3.1" + }, + { + "name": "itsdangerous", + "version": "2.0.1", + "purl": "pkg:pypi/itsdangerous@2.0.1", + "type": "library", + "bom-ref": "pkg:pypi/itsdangerous@2.0.1" + }, + { + "name": "jinja2", + "version": "3.0.3", + "purl": "pkg:pypi/jinja2@3.0.3", + "type": "library", + "bom-ref": "pkg:pypi/jinja2@3.0.3" + }, + { + "name": "werkzeug", + "version": "2.0.3", + "purl": "pkg:pypi/werkzeug@2.0.3", + "type": "library", + "bom-ref": "pkg:pypi/werkzeug@2.0.3" + }, + { + "name": "certifi", + "version": "2023.7.22", + "purl": "pkg:pypi/certifi@2023.7.22", + "type": "library", + "bom-ref": "pkg:pypi/certifi@2023.7.22" + }, + { + "name": "chardet", + "version": "4.0.0", + "purl": "pkg:pypi/chardet@4.0.0", + "type": "library", + "bom-ref": "pkg:pypi/chardet@4.0.0" + }, + { + "name": "idna", + "version": "2.10", + "purl": "pkg:pypi/idna@2.10", + "type": "library", + "bom-ref": "pkg:pypi/idna@2.10" + }, + { + "name": "urllib3", + "version": "1.26.16", + "purl": "pkg:pypi/urllib3@1.26.16", + "type": "library", + "bom-ref": "pkg:pypi/urllib3@1.26.16" + } + ], + "dependencies": [ + { + "ref": "pkg:pypi/test-project@1.0.0", + "dependsOn": [ + "pkg:pypi/flask@2.0.3", + "pkg:pypi/requests@2.25.1", + "pkg:pypi/typing-extensions@4.1.1" + ] + }, + { + "ref": "pkg:pypi/flask@2.0.3", + "dependsOn": [ + "pkg:pypi/click@8.3.1", + "pkg:pypi/itsdangerous@2.0.1", + "pkg:pypi/jinja2@3.0.3", + "pkg:pypi/werkzeug@2.0.3" + ] + }, + { + "ref": "pkg:pypi/requests@2.25.1", + "dependsOn": [ + "pkg:pypi/certifi@2023.7.22", + "pkg:pypi/chardet@4.0.0", + "pkg:pypi/idna@2.10", + "pkg:pypi/urllib3@1.26.16" + ] + }, + { + "ref": "pkg:pypi/typing-extensions@4.1.1", + "dependsOn": [] + }, + { + "ref": "pkg:pypi/click@8.3.1", + "dependsOn": [] + }, + { + "ref": "pkg:pypi/itsdangerous@2.0.1", + "dependsOn": [] + }, + { + "ref": "pkg:pypi/jinja2@3.0.3", + "dependsOn": [] + }, + { + "ref": "pkg:pypi/werkzeug@2.0.3", + "dependsOn": [] + }, + { + "ref": "pkg:pypi/certifi@2023.7.22", + "dependsOn": [] + }, + { + "ref": "pkg:pypi/chardet@4.0.0", + "dependsOn": [] + }, + { + "ref": "pkg:pypi/idna@2.10", + "dependsOn": [] + }, + { + "ref": "pkg:pypi/urllib3@1.26.16", + "dependsOn": [] + } + ] } diff --git a/test/providers/tst_manifests/pyproject/poetry_marker_filtering/poetry.lock b/test/providers/tst_manifests/pyproject/poetry_marker_filtering/poetry.lock new file mode 100644 index 00000000..c5241584 --- /dev/null +++ b/test/providers/tst_manifests/pyproject/poetry_marker_filtering/poetry.lock @@ -0,0 +1,63 @@ +# This file is automatically @generated by Poetry and should not be changed by hand. + +[[package]] +name = "click" +version = "8.3.1" +description = "Composable command line interface toolkit" +optional = false +python-versions = ">=3.7" +groups = ["main"] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = ">=2.7" +groups = ["main"] + +[package.files] +colorama = [] + +[[package]] +name = "six" +version = "1.16.0" +description = "Python 2 and 3 compatibility utilities" +optional = false +python-versions = ">=2.7" +groups = ["main"] + +[package.files] +six = [] + +[[package]] +name = "win32-setctime" +version = "1.1.0" +description = "A small Python utility to set file creation time on Windows" +optional = false +python-versions = ">=3.5" +groups = ["main"] + +[package.dependencies] + +[package.files] +win32-setctime = [] + +[[package]] +name = "pyreadline3" +version = "3.4.1" +description = "A Python implementation of GNU readline." +optional = false +python-versions = ">=3.8" +groups = ["main"] + +[package.files] +pyreadline3 = [] + +[metadata] +lock-version = "2.1" +python-versions = "^3.8" +content-hash = "dummy" diff --git a/test/providers/tst_manifests/pyproject/poetry_marker_filtering/pyproject.toml b/test/providers/tst_manifests/pyproject/poetry_marker_filtering/pyproject.toml new file mode 100644 index 00000000..fed9aa21 --- /dev/null +++ b/test/providers/tst_manifests/pyproject/poetry_marker_filtering/pyproject.toml @@ -0,0 +1,15 @@ +[project] +name = "marker-test" +version = "1.0.0" +dependencies = [ + "click==8.3.1", + "six==1.16.0", + "win32-setctime==1.1.0; os_name == 'nt'", + "pyreadline3==3.4.1; sys_platform == 'win32'", +] + +[tool.poetry.dependencies] +python = "^3.8" + +[tool.poetry] +name = "marker-test" diff --git a/test/providers/tst_manifests/pyproject/poetry_only_deps/expected_component_sbom.json b/test/providers/tst_manifests/pyproject/poetry_only_deps/expected_component_sbom.json index 06737c4e..37b91266 100644 --- a/test/providers/tst_manifests/pyproject/poetry_only_deps/expected_component_sbom.json +++ b/test/providers/tst_manifests/pyproject/poetry_only_deps/expected_component_sbom.json @@ -1,60 +1,60 @@ { - "bomFormat": "CycloneDX", - "specVersion": "1.4", - "version": 1, - "metadata": { - "timestamp": "2023-10-01T00:00:00.000Z", - "component": { - "name": "poetry-only-project", - "version": "1.0.0", - "purl": "pkg:pypi/poetry-only-project@1.0.0", - "type": "application", - "bom-ref": "pkg:pypi/poetry-only-project@1.0.0" - } - }, - "components": [ - { - "name": "click", - "version": "8.1.8", - "purl": "pkg:pypi/click@8.1.8", - "type": "library", - "bom-ref": "pkg:pypi/click@8.1.8" - }, - { - "name": "flask", - "version": "2.3.3", - "purl": "pkg:pypi/flask@2.3.3", - "type": "library", - "bom-ref": "pkg:pypi/flask@2.3.3" - }, - { - "name": "requests", - "version": "2.32.4", - "purl": "pkg:pypi/requests@2.32.4", - "type": "library", - "bom-ref": "pkg:pypi/requests@2.32.4" - } - ], - "dependencies": [ - { - "ref": "pkg:pypi/poetry-only-project@1.0.0", - "dependsOn": [ - "pkg:pypi/click@8.1.8", - "pkg:pypi/flask@2.3.3", - "pkg:pypi/requests@2.32.4" - ] - }, - { - "ref": "pkg:pypi/click@8.1.8", - "dependsOn": [] - }, - { - "ref": "pkg:pypi/flask@2.3.3", - "dependsOn": [] - }, - { - "ref": "pkg:pypi/requests@2.32.4", - "dependsOn": [] - } - ] + "bomFormat": "CycloneDX", + "specVersion": "1.4", + "version": 1, + "metadata": { + "timestamp": "2023-10-01T00:00:00.000Z", + "component": { + "name": "poetry-only-project", + "version": "1.0.0", + "purl": "pkg:pypi/poetry-only-project@1.0.0", + "type": "application", + "bom-ref": "pkg:pypi/poetry-only-project@1.0.0" + } + }, + "components": [ + { + "name": "click", + "version": "8.1.8", + "purl": "pkg:pypi/click@8.1.8", + "type": "library", + "bom-ref": "pkg:pypi/click@8.1.8" + }, + { + "name": "flask", + "version": "2.3.3", + "purl": "pkg:pypi/flask@2.3.3", + "type": "library", + "bom-ref": "pkg:pypi/flask@2.3.3" + }, + { + "name": "requests", + "version": "2.32.4", + "purl": "pkg:pypi/requests@2.32.4", + "type": "library", + "bom-ref": "pkg:pypi/requests@2.32.4" + } + ], + "dependencies": [ + { + "ref": "pkg:pypi/poetry-only-project@1.0.0", + "dependsOn": [ + "pkg:pypi/click@8.1.8", + "pkg:pypi/flask@2.3.3", + "pkg:pypi/requests@2.32.4" + ] + }, + { + "ref": "pkg:pypi/click@8.1.8", + "dependsOn": [] + }, + { + "ref": "pkg:pypi/flask@2.3.3", + "dependsOn": [] + }, + { + "ref": "pkg:pypi/requests@2.32.4", + "dependsOn": [] + } + ] } diff --git a/test/providers/tst_manifests/pyproject/poetry_only_deps/expected_stack_sbom.json b/test/providers/tst_manifests/pyproject/poetry_only_deps/expected_stack_sbom.json index 31d20f56..d36acbfa 100644 --- a/test/providers/tst_manifests/pyproject/poetry_only_deps/expected_stack_sbom.json +++ b/test/providers/tst_manifests/pyproject/poetry_only_deps/expected_stack_sbom.json @@ -1,224 +1,199 @@ { - "bomFormat": "CycloneDX", - "specVersion": "1.4", - "version": 1, - "metadata": { - "timestamp": "2023-10-01T00:00:00.000Z", - "component": { - "name": "poetry-only-project", - "version": "1.0.0", - "purl": "pkg:pypi/poetry-only-project@1.0.0", - "type": "application", - "bom-ref": "pkg:pypi/poetry-only-project@1.0.0" - } - }, - "components": [ - { - "name": "click", - "version": "8.1.8", - "purl": "pkg:pypi/click@8.1.8", - "type": "library", - "bom-ref": "pkg:pypi/click@8.1.8" - }, - { - "name": "flask", - "version": "2.3.3", - "purl": "pkg:pypi/flask@2.3.3", - "type": "library", - "bom-ref": "pkg:pypi/flask@2.3.3" - }, - { - "name": "requests", - "version": "2.32.4", - "purl": "pkg:pypi/requests@2.32.4", - "type": "library", - "bom-ref": "pkg:pypi/requests@2.32.4" - }, - { - "name": "colorama", - "version": "0.4.6", - "purl": "pkg:pypi/colorama@0.4.6", - "type": "library", - "bom-ref": "pkg:pypi/colorama@0.4.6" - }, - { - "name": "blinker", - "version": "1.8.2", - "purl": "pkg:pypi/blinker@1.8.2", - "type": "library", - "bom-ref": "pkg:pypi/blinker@1.8.2" - }, - { - "name": "importlib-metadata", - "version": "8.5.0", - "purl": "pkg:pypi/importlib-metadata@8.5.0", - "type": "library", - "bom-ref": "pkg:pypi/importlib-metadata@8.5.0" - }, - { - "name": "itsdangerous", - "version": "2.2.0", - "purl": "pkg:pypi/itsdangerous@2.2.0", - "type": "library", - "bom-ref": "pkg:pypi/itsdangerous@2.2.0" - }, - { - "name": "jinja2", - "version": "3.1.6", - "purl": "pkg:pypi/jinja2@3.1.6", - "type": "library", - "bom-ref": "pkg:pypi/jinja2@3.1.6" - }, - { - "name": "werkzeug", - "version": "3.0.6", - "purl": "pkg:pypi/werkzeug@3.0.6", - "type": "library", - "bom-ref": "pkg:pypi/werkzeug@3.0.6" - }, - { - "name": "zipp", - "version": "3.20.2", - "purl": "pkg:pypi/zipp@3.20.2", - "type": "library", - "bom-ref": "pkg:pypi/zipp@3.20.2" - }, - { - "name": "markupsafe", - "version": "2.1.5", - "purl": "pkg:pypi/markupsafe@2.1.5", - "type": "library", - "bom-ref": "pkg:pypi/markupsafe@2.1.5" - }, - { - "name": "certifi", - "version": "2026.2.25", - "purl": "pkg:pypi/certifi@2026.2.25", - "type": "library", - "bom-ref": "pkg:pypi/certifi@2026.2.25" - }, - { - "name": "charset-normalizer", - "version": "3.4.6", - "purl": "pkg:pypi/charset-normalizer@3.4.6", - "type": "library", - "bom-ref": "pkg:pypi/charset-normalizer@3.4.6" - }, - { - "name": "idna", - "version": "3.11", - "purl": "pkg:pypi/idna@3.11", - "type": "library", - "bom-ref": "pkg:pypi/idna@3.11" - }, - { - "name": "pysocks", - "version": "1.7.1", - "purl": "pkg:pypi/pysocks@1.7.1", - "type": "library", - "bom-ref": "pkg:pypi/pysocks@1.7.1" - }, - { - "name": "urllib3", - "version": "2.2.3", - "purl": "pkg:pypi/urllib3@2.2.3", - "type": "library", - "bom-ref": "pkg:pypi/urllib3@2.2.3" - } - ], - "dependencies": [ - { - "ref": "pkg:pypi/poetry-only-project@1.0.0", - "dependsOn": [ - "pkg:pypi/click@8.1.8", - "pkg:pypi/flask@2.3.3", - "pkg:pypi/requests@2.32.4" - ] - }, - { - "ref": "pkg:pypi/click@8.1.8", - "dependsOn": [ - "pkg:pypi/colorama@0.4.6" - ] - }, - { - "ref": "pkg:pypi/flask@2.3.3", - "dependsOn": [ - "pkg:pypi/blinker@1.8.2", - "pkg:pypi/click@8.1.8", - "pkg:pypi/importlib-metadata@8.5.0", - "pkg:pypi/itsdangerous@2.2.0", - "pkg:pypi/jinja2@3.1.6", - "pkg:pypi/werkzeug@3.0.6" - ] - }, - { - "ref": "pkg:pypi/requests@2.32.4", - "dependsOn": [ - "pkg:pypi/certifi@2026.2.25", - "pkg:pypi/charset-normalizer@3.4.6", - "pkg:pypi/idna@3.11", - "pkg:pypi/pysocks@1.7.1", - "pkg:pypi/urllib3@2.2.3" - ] - }, - { - "ref": "pkg:pypi/colorama@0.4.6", - "dependsOn": [] - }, - { - "ref": "pkg:pypi/blinker@1.8.2", - "dependsOn": [] - }, - { - "ref": "pkg:pypi/importlib-metadata@8.5.0", - "dependsOn": [ - "pkg:pypi/zipp@3.20.2" - ] - }, - { - "ref": "pkg:pypi/itsdangerous@2.2.0", - "dependsOn": [] - }, - { - "ref": "pkg:pypi/jinja2@3.1.6", - "dependsOn": [ - "pkg:pypi/markupsafe@2.1.5" - ] - }, - { - "ref": "pkg:pypi/werkzeug@3.0.6", - "dependsOn": [ - "pkg:pypi/markupsafe@2.1.5" - ] - }, - { - "ref": "pkg:pypi/zipp@3.20.2", - "dependsOn": [] - }, - { - "ref": "pkg:pypi/markupsafe@2.1.5", - "dependsOn": [] - }, - { - "ref": "pkg:pypi/certifi@2026.2.25", - "dependsOn": [] - }, - { - "ref": "pkg:pypi/charset-normalizer@3.4.6", - "dependsOn": [] - }, - { - "ref": "pkg:pypi/idna@3.11", - "dependsOn": [] - }, - { - "ref": "pkg:pypi/pysocks@1.7.1", - "dependsOn": [] - }, - { - "ref": "pkg:pypi/urllib3@2.2.3", - "dependsOn": [] - } - ] + "bomFormat": "CycloneDX", + "specVersion": "1.4", + "version": 1, + "metadata": { + "timestamp": "2023-10-01T00:00:00.000Z", + "component": { + "name": "poetry-only-project", + "version": "1.0.0", + "purl": "pkg:pypi/poetry-only-project@1.0.0", + "type": "application", + "bom-ref": "pkg:pypi/poetry-only-project@1.0.0" + } + }, + "components": [ + { + "name": "click", + "version": "8.1.8", + "purl": "pkg:pypi/click@8.1.8", + "type": "library", + "bom-ref": "pkg:pypi/click@8.1.8" + }, + { + "name": "flask", + "version": "2.3.3", + "purl": "pkg:pypi/flask@2.3.3", + "type": "library", + "bom-ref": "pkg:pypi/flask@2.3.3" + }, + { + "name": "requests", + "version": "2.32.4", + "purl": "pkg:pypi/requests@2.32.4", + "type": "library", + "bom-ref": "pkg:pypi/requests@2.32.4" + }, + { + "name": "blinker", + "version": "1.8.2", + "purl": "pkg:pypi/blinker@1.8.2", + "type": "library", + "bom-ref": "pkg:pypi/blinker@1.8.2" + }, + { + "name": "importlib-metadata", + "version": "8.5.0", + "purl": "pkg:pypi/importlib-metadata@8.5.0", + "type": "library", + "bom-ref": "pkg:pypi/importlib-metadata@8.5.0" + }, + { + "name": "itsdangerous", + "version": "2.2.0", + "purl": "pkg:pypi/itsdangerous@2.2.0", + "type": "library", + "bom-ref": "pkg:pypi/itsdangerous@2.2.0" + }, + { + "name": "jinja2", + "version": "3.1.6", + "purl": "pkg:pypi/jinja2@3.1.6", + "type": "library", + "bom-ref": "pkg:pypi/jinja2@3.1.6" + }, + { + "name": "werkzeug", + "version": "3.0.6", + "purl": "pkg:pypi/werkzeug@3.0.6", + "type": "library", + "bom-ref": "pkg:pypi/werkzeug@3.0.6" + }, + { + "name": "zipp", + "version": "3.20.2", + "purl": "pkg:pypi/zipp@3.20.2", + "type": "library", + "bom-ref": "pkg:pypi/zipp@3.20.2" + }, + { + "name": "markupsafe", + "version": "2.1.5", + "purl": "pkg:pypi/markupsafe@2.1.5", + "type": "library", + "bom-ref": "pkg:pypi/markupsafe@2.1.5" + }, + { + "name": "certifi", + "version": "2026.2.25", + "purl": "pkg:pypi/certifi@2026.2.25", + "type": "library", + "bom-ref": "pkg:pypi/certifi@2026.2.25" + }, + { + "name": "charset-normalizer", + "version": "3.4.6", + "purl": "pkg:pypi/charset-normalizer@3.4.6", + "type": "library", + "bom-ref": "pkg:pypi/charset-normalizer@3.4.6" + }, + { + "name": "idna", + "version": "3.11", + "purl": "pkg:pypi/idna@3.11", + "type": "library", + "bom-ref": "pkg:pypi/idna@3.11" + }, + { + "name": "urllib3", + "version": "2.2.3", + "purl": "pkg:pypi/urllib3@2.2.3", + "type": "library", + "bom-ref": "pkg:pypi/urllib3@2.2.3" + } + ], + "dependencies": [ + { + "ref": "pkg:pypi/poetry-only-project@1.0.0", + "dependsOn": [ + "pkg:pypi/click@8.1.8", + "pkg:pypi/flask@2.3.3", + "pkg:pypi/requests@2.32.4" + ] + }, + { + "ref": "pkg:pypi/click@8.1.8", + "dependsOn": [] + }, + { + "ref": "pkg:pypi/flask@2.3.3", + "dependsOn": [ + "pkg:pypi/blinker@1.8.2", + "pkg:pypi/click@8.1.8", + "pkg:pypi/importlib-metadata@8.5.0", + "pkg:pypi/itsdangerous@2.2.0", + "pkg:pypi/jinja2@3.1.6", + "pkg:pypi/werkzeug@3.0.6" + ] + }, + { + "ref": "pkg:pypi/requests@2.32.4", + "dependsOn": [ + "pkg:pypi/certifi@2026.2.25", + "pkg:pypi/charset-normalizer@3.4.6", + "pkg:pypi/idna@3.11", + "pkg:pypi/urllib3@2.2.3" + ] + }, + { + "ref": "pkg:pypi/blinker@1.8.2", + "dependsOn": [] + }, + { + "ref": "pkg:pypi/importlib-metadata@8.5.0", + "dependsOn": [ + "pkg:pypi/zipp@3.20.2" + ] + }, + { + "ref": "pkg:pypi/itsdangerous@2.2.0", + "dependsOn": [] + }, + { + "ref": "pkg:pypi/jinja2@3.1.6", + "dependsOn": [ + "pkg:pypi/markupsafe@2.1.5" + ] + }, + { + "ref": "pkg:pypi/werkzeug@3.0.6", + "dependsOn": [ + "pkg:pypi/markupsafe@2.1.5" + ] + }, + { + "ref": "pkg:pypi/zipp@3.20.2", + "dependsOn": [] + }, + { + "ref": "pkg:pypi/markupsafe@2.1.5", + "dependsOn": [] + }, + { + "ref": "pkg:pypi/certifi@2026.2.25", + "dependsOn": [] + }, + { + "ref": "pkg:pypi/charset-normalizer@3.4.6", + "dependsOn": [] + }, + { + "ref": "pkg:pypi/idna@3.11", + "dependsOn": [] + }, + { + "ref": "pkg:pypi/urllib3@2.2.3", + "dependsOn": [] + } + ] } diff --git a/test/providers/tst_manifests/pyproject/uv_lock/expected_component_sbom.json b/test/providers/tst_manifests/pyproject/uv_lock/expected_component_sbom.json index 7d952815..b0b1108e 100644 --- a/test/providers/tst_manifests/pyproject/uv_lock/expected_component_sbom.json +++ b/test/providers/tst_manifests/pyproject/uv_lock/expected_component_sbom.json @@ -1,48 +1,48 @@ { - "bomFormat": "CycloneDX", - "specVersion": "1.4", - "version": 1, - "metadata": { - "timestamp": "2023-10-01T00:00:00.000Z", - "component": { - "name": "test-project", - "version": "1.0.0", - "purl": "pkg:pypi/test-project@1.0.0", - "type": "application", - "bom-ref": "pkg:pypi/test-project@1.0.0" - } - }, - "components": [ - { - "name": "flask", - "version": "2.0.3", - "purl": "pkg:pypi/flask@2.0.3", - "type": "library", - "bom-ref": "pkg:pypi/flask@2.0.3" - }, - { - "name": "requests", - "version": "2.25.1", - "purl": "pkg:pypi/requests@2.25.1", - "type": "library", - "bom-ref": "pkg:pypi/requests@2.25.1" - } - ], - "dependencies": [ - { - "ref": "pkg:pypi/test-project@1.0.0", - "dependsOn": [ - "pkg:pypi/flask@2.0.3", - "pkg:pypi/requests@2.25.1" - ] - }, - { - "ref": "pkg:pypi/flask@2.0.3", - "dependsOn": [] - }, - { - "ref": "pkg:pypi/requests@2.25.1", - "dependsOn": [] - } - ] + "bomFormat": "CycloneDX", + "specVersion": "1.4", + "version": 1, + "metadata": { + "timestamp": "2023-10-01T00:00:00.000Z", + "component": { + "name": "test-project", + "version": "1.0.0", + "purl": "pkg:pypi/test-project@1.0.0", + "type": "application", + "bom-ref": "pkg:pypi/test-project@1.0.0" + } + }, + "components": [ + { + "name": "flask", + "version": "2.0.3", + "purl": "pkg:pypi/flask@2.0.3", + "type": "library", + "bom-ref": "pkg:pypi/flask@2.0.3" + }, + { + "name": "requests", + "version": "2.25.1", + "purl": "pkg:pypi/requests@2.25.1", + "type": "library", + "bom-ref": "pkg:pypi/requests@2.25.1" + } + ], + "dependencies": [ + { + "ref": "pkg:pypi/test-project@1.0.0", + "dependsOn": [ + "pkg:pypi/flask@2.0.3", + "pkg:pypi/requests@2.25.1" + ] + }, + { + "ref": "pkg:pypi/flask@2.0.3", + "dependsOn": [] + }, + { + "ref": "pkg:pypi/requests@2.25.1", + "dependsOn": [] + } + ] } diff --git a/test/providers/tst_manifests/pyproject/uv_lock/expected_stack_sbom.json b/test/providers/tst_manifests/pyproject/uv_lock/expected_stack_sbom.json index 9f90d870..b8211ba4 100644 --- a/test/providers/tst_manifests/pyproject/uv_lock/expected_stack_sbom.json +++ b/test/providers/tst_manifests/pyproject/uv_lock/expected_stack_sbom.json @@ -1,172 +1,159 @@ { - "bomFormat": "CycloneDX", - "specVersion": "1.4", - "version": 1, - "metadata": { - "timestamp": "2023-10-01T00:00:00.000Z", - "component": { - "name": "test-project", - "version": "1.0.0", - "purl": "pkg:pypi/test-project@1.0.0", - "type": "application", - "bom-ref": "pkg:pypi/test-project@1.0.0" - } - }, - "components": [ - { - "name": "flask", - "version": "2.0.3", - "purl": "pkg:pypi/flask@2.0.3", - "type": "library", - "bom-ref": "pkg:pypi/flask@2.0.3" - }, - { - "name": "requests", - "version": "2.25.1", - "purl": "pkg:pypi/requests@2.25.1", - "type": "library", - "bom-ref": "pkg:pypi/requests@2.25.1" - }, - { - "name": "click", - "version": "8.3.1", - "purl": "pkg:pypi/click@8.3.1", - "type": "library", - "bom-ref": "pkg:pypi/click@8.3.1" - }, - { - "name": "colorama", - "version": "0.4.6", - "purl": "pkg:pypi/colorama@0.4.6", - "type": "library", - "bom-ref": "pkg:pypi/colorama@0.4.6" - }, - { - "name": "itsdangerous", - "version": "2.0.1", - "purl": "pkg:pypi/itsdangerous@2.0.1", - "type": "library", - "bom-ref": "pkg:pypi/itsdangerous@2.0.1" - }, - { - "name": "jinja2", - "version": "3.0.3", - "purl": "pkg:pypi/jinja2@3.0.3", - "type": "library", - "bom-ref": "pkg:pypi/jinja2@3.0.3" - }, - { - "name": "werkzeug", - "version": "2.0.3", - "purl": "pkg:pypi/werkzeug@2.0.3", - "type": "library", - "bom-ref": "pkg:pypi/werkzeug@2.0.3" - }, - { - "name": "markupsafe", - "version": "2.0.1", - "purl": "pkg:pypi/markupsafe@2.0.1", - "type": "library", - "bom-ref": "pkg:pypi/markupsafe@2.0.1" - }, - { - "name": "certifi", - "version": "2023.7.22", - "purl": "pkg:pypi/certifi@2023.7.22", - "type": "library", - "bom-ref": "pkg:pypi/certifi@2023.7.22" - }, - { - "name": "chardet", - "version": "4.0.0", - "purl": "pkg:pypi/chardet@4.0.0", - "type": "library", - "bom-ref": "pkg:pypi/chardet@4.0.0" - }, - { - "name": "idna", - "version": "2.10", - "purl": "pkg:pypi/idna@2.10", - "type": "library", - "bom-ref": "pkg:pypi/idna@2.10" - }, - { - "name": "urllib3", - "version": "1.26.16", - "purl": "pkg:pypi/urllib3@1.26.16", - "type": "library", - "bom-ref": "pkg:pypi/urllib3@1.26.16" - } - ], - "dependencies": [ - { - "ref": "pkg:pypi/test-project@1.0.0", - "dependsOn": [ - "pkg:pypi/flask@2.0.3", - "pkg:pypi/requests@2.25.1" - ] - }, - { - "ref": "pkg:pypi/flask@2.0.3", - "dependsOn": [ - "pkg:pypi/click@8.3.1", - "pkg:pypi/itsdangerous@2.0.1", - "pkg:pypi/jinja2@3.0.3", - "pkg:pypi/werkzeug@2.0.3" - ] - }, - { - "ref": "pkg:pypi/requests@2.25.1", - "dependsOn": [ - "pkg:pypi/certifi@2023.7.22", - "pkg:pypi/chardet@4.0.0", - "pkg:pypi/idna@2.10", - "pkg:pypi/urllib3@1.26.16" - ] - }, - { - "ref": "pkg:pypi/click@8.3.1", - "dependsOn": [ - "pkg:pypi/colorama@0.4.6" - ] - }, - { - "ref": "pkg:pypi/colorama@0.4.6", - "dependsOn": [] - }, - { - "ref": "pkg:pypi/itsdangerous@2.0.1", - "dependsOn": [] - }, - { - "ref": "pkg:pypi/jinja2@3.0.3", - "dependsOn": [ - "pkg:pypi/markupsafe@2.0.1" - ] - }, - { - "ref": "pkg:pypi/werkzeug@2.0.3", - "dependsOn": [] - }, - { - "ref": "pkg:pypi/markupsafe@2.0.1", - "dependsOn": [] - }, - { - "ref": "pkg:pypi/certifi@2023.7.22", - "dependsOn": [] - }, - { - "ref": "pkg:pypi/chardet@4.0.0", - "dependsOn": [] - }, - { - "ref": "pkg:pypi/idna@2.10", - "dependsOn": [] - }, - { - "ref": "pkg:pypi/urllib3@1.26.16", - "dependsOn": [] - } - ] + "bomFormat": "CycloneDX", + "specVersion": "1.4", + "version": 1, + "metadata": { + "timestamp": "2023-10-01T00:00:00.000Z", + "component": { + "name": "test-project", + "version": "1.0.0", + "purl": "pkg:pypi/test-project@1.0.0", + "type": "application", + "bom-ref": "pkg:pypi/test-project@1.0.0" + } + }, + "components": [ + { + "name": "flask", + "version": "2.0.3", + "purl": "pkg:pypi/flask@2.0.3", + "type": "library", + "bom-ref": "pkg:pypi/flask@2.0.3" + }, + { + "name": "requests", + "version": "2.25.1", + "purl": "pkg:pypi/requests@2.25.1", + "type": "library", + "bom-ref": "pkg:pypi/requests@2.25.1" + }, + { + "name": "click", + "version": "8.3.1", + "purl": "pkg:pypi/click@8.3.1", + "type": "library", + "bom-ref": "pkg:pypi/click@8.3.1" + }, + { + "name": "itsdangerous", + "version": "2.0.1", + "purl": "pkg:pypi/itsdangerous@2.0.1", + "type": "library", + "bom-ref": "pkg:pypi/itsdangerous@2.0.1" + }, + { + "name": "jinja2", + "version": "3.0.3", + "purl": "pkg:pypi/jinja2@3.0.3", + "type": "library", + "bom-ref": "pkg:pypi/jinja2@3.0.3" + }, + { + "name": "werkzeug", + "version": "2.0.3", + "purl": "pkg:pypi/werkzeug@2.0.3", + "type": "library", + "bom-ref": "pkg:pypi/werkzeug@2.0.3" + }, + { + "name": "markupsafe", + "version": "2.0.1", + "purl": "pkg:pypi/markupsafe@2.0.1", + "type": "library", + "bom-ref": "pkg:pypi/markupsafe@2.0.1" + }, + { + "name": "certifi", + "version": "2023.7.22", + "purl": "pkg:pypi/certifi@2023.7.22", + "type": "library", + "bom-ref": "pkg:pypi/certifi@2023.7.22" + }, + { + "name": "chardet", + "version": "4.0.0", + "purl": "pkg:pypi/chardet@4.0.0", + "type": "library", + "bom-ref": "pkg:pypi/chardet@4.0.0" + }, + { + "name": "idna", + "version": "2.10", + "purl": "pkg:pypi/idna@2.10", + "type": "library", + "bom-ref": "pkg:pypi/idna@2.10" + }, + { + "name": "urllib3", + "version": "1.26.16", + "purl": "pkg:pypi/urllib3@1.26.16", + "type": "library", + "bom-ref": "pkg:pypi/urllib3@1.26.16" + } + ], + "dependencies": [ + { + "ref": "pkg:pypi/test-project@1.0.0", + "dependsOn": [ + "pkg:pypi/flask@2.0.3", + "pkg:pypi/requests@2.25.1" + ] + }, + { + "ref": "pkg:pypi/flask@2.0.3", + "dependsOn": [ + "pkg:pypi/click@8.3.1", + "pkg:pypi/itsdangerous@2.0.1", + "pkg:pypi/jinja2@3.0.3", + "pkg:pypi/werkzeug@2.0.3" + ] + }, + { + "ref": "pkg:pypi/requests@2.25.1", + "dependsOn": [ + "pkg:pypi/certifi@2023.7.22", + "pkg:pypi/chardet@4.0.0", + "pkg:pypi/idna@2.10", + "pkg:pypi/urllib3@1.26.16" + ] + }, + { + "ref": "pkg:pypi/click@8.3.1", + "dependsOn": [] + }, + { + "ref": "pkg:pypi/itsdangerous@2.0.1", + "dependsOn": [] + }, + { + "ref": "pkg:pypi/jinja2@3.0.3", + "dependsOn": [ + "pkg:pypi/markupsafe@2.0.1" + ] + }, + { + "ref": "pkg:pypi/werkzeug@2.0.3", + "dependsOn": [] + }, + { + "ref": "pkg:pypi/markupsafe@2.0.1", + "dependsOn": [] + }, + { + "ref": "pkg:pypi/certifi@2023.7.22", + "dependsOn": [] + }, + { + "ref": "pkg:pypi/chardet@4.0.0", + "dependsOn": [] + }, + { + "ref": "pkg:pypi/idna@2.10", + "dependsOn": [] + }, + { + "ref": "pkg:pypi/urllib3@1.26.16", + "dependsOn": [] + } + ] } diff --git a/test/providers/tst_manifests/pyproject/uv_marker_filtering/pyproject.toml b/test/providers/tst_manifests/pyproject/uv_marker_filtering/pyproject.toml new file mode 100644 index 00000000..9ee518bc --- /dev/null +++ b/test/providers/tst_manifests/pyproject/uv_marker_filtering/pyproject.toml @@ -0,0 +1,7 @@ +[project] +name = "marker-test" +version = "1.0.0" +dependencies = [ + "click==8.3.1", + "six==1.16.0", +] diff --git a/test/providers/tst_manifests/pyproject/uv_marker_filtering/uv.lock b/test/providers/tst_manifests/pyproject/uv_marker_filtering/uv.lock new file mode 100644 index 00000000..647efe99 --- /dev/null +++ b/test/providers/tst_manifests/pyproject/uv_marker_filtering/uv.lock @@ -0,0 +1,44 @@ +version = 1 +requires-python = ">=3.8" + +[[package]] +name = "marker-test" +version = "1.0.0" +source = { virtual = "." } +dependencies = [ + { name = "click" }, + { name = "six" }, +] + +[[package]] +name = "click" +version = "8.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } + +[[package]] +name = "six" +version = "1.16.0" +source = { registry = "https://pypi.org/simple" } + +[[package]] +name = "win32-setctime" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } + +[[package]] +name = "pyreadline3" +version = "3.4.1" +source = { registry = "https://pypi.org/simple" } + +[[package]] +name = "pyobjc" +version = "10.1" +source = { registry = "https://pypi.org/simple" } diff --git a/test/providers/tst_manifests/pyproject/uv_self_ref/expected_component_sbom.json b/test/providers/tst_manifests/pyproject/uv_self_ref/expected_component_sbom.json index fa3afe72..6aacd266 100644 --- a/test/providers/tst_manifests/pyproject/uv_self_ref/expected_component_sbom.json +++ b/test/providers/tst_manifests/pyproject/uv_self_ref/expected_component_sbom.json @@ -1,96 +1,96 @@ { - "bomFormat": "CycloneDX", - "specVersion": "1.4", - "version": 1, - "metadata": { - "timestamp": "2023-10-01T00:00:00.000Z", - "component": { - "name": "test-project", - "version": "0.1.0", - "purl": "pkg:pypi/test-project@0.1.0", - "type": "application", - "bom-ref": "pkg:pypi/test-project@0.1.0" - } - }, - "components": [ - { - "name": "beautifulsoup4", - "version": "4.12.2", - "purl": "pkg:pypi/beautifulsoup4@4.12.2", - "type": "library", - "bom-ref": "pkg:pypi/beautifulsoup4@4.12.2" - }, - { - "name": "certifi", - "version": "2023.7.22", - "purl": "pkg:pypi/certifi@2023.7.22", - "type": "library", - "bom-ref": "pkg:pypi/certifi@2023.7.22" - }, - { - "name": "click", - "version": "8.0.4", - "purl": "pkg:pypi/click@8.0.4", - "type": "library", - "bom-ref": "pkg:pypi/click@8.0.4" - }, - { - "name": "flask", - "version": "2.0.3", - "purl": "pkg:pypi/flask@2.0.3", - "type": "library", - "bom-ref": "pkg:pypi/flask@2.0.3" - }, - { - "name": "requests", - "version": "2.25.1", - "purl": "pkg:pypi/requests@2.25.1", - "type": "library", - "bom-ref": "pkg:pypi/requests@2.25.1" - }, - { - "name": "uvicorn", - "version": "0.17.0", - "purl": "pkg:pypi/uvicorn@0.17.0", - "type": "library", - "bom-ref": "pkg:pypi/uvicorn@0.17.0" - } - ], - "dependencies": [ - { - "ref": "pkg:pypi/test-project@0.1.0", - "dependsOn": [ - "pkg:pypi/beautifulsoup4@4.12.2", - "pkg:pypi/certifi@2023.7.22", - "pkg:pypi/click@8.0.4", - "pkg:pypi/flask@2.0.3", - "pkg:pypi/requests@2.25.1", - "pkg:pypi/uvicorn@0.17.0" - ] - }, - { - "ref": "pkg:pypi/beautifulsoup4@4.12.2", - "dependsOn": [] - }, - { - "ref": "pkg:pypi/certifi@2023.7.22", - "dependsOn": [] - }, - { - "ref": "pkg:pypi/click@8.0.4", - "dependsOn": [] - }, - { - "ref": "pkg:pypi/flask@2.0.3", - "dependsOn": [] - }, - { - "ref": "pkg:pypi/requests@2.25.1", - "dependsOn": [] - }, - { - "ref": "pkg:pypi/uvicorn@0.17.0", - "dependsOn": [] - } - ] + "bomFormat": "CycloneDX", + "specVersion": "1.4", + "version": 1, + "metadata": { + "timestamp": "2023-10-01T00:00:00.000Z", + "component": { + "name": "test-project", + "version": "0.1.0", + "purl": "pkg:pypi/test-project@0.1.0", + "type": "application", + "bom-ref": "pkg:pypi/test-project@0.1.0" + } + }, + "components": [ + { + "name": "beautifulsoup4", + "version": "4.12.2", + "purl": "pkg:pypi/beautifulsoup4@4.12.2", + "type": "library", + "bom-ref": "pkg:pypi/beautifulsoup4@4.12.2" + }, + { + "name": "certifi", + "version": "2023.7.22", + "purl": "pkg:pypi/certifi@2023.7.22", + "type": "library", + "bom-ref": "pkg:pypi/certifi@2023.7.22" + }, + { + "name": "click", + "version": "8.0.4", + "purl": "pkg:pypi/click@8.0.4", + "type": "library", + "bom-ref": "pkg:pypi/click@8.0.4" + }, + { + "name": "flask", + "version": "2.0.3", + "purl": "pkg:pypi/flask@2.0.3", + "type": "library", + "bom-ref": "pkg:pypi/flask@2.0.3" + }, + { + "name": "requests", + "version": "2.25.1", + "purl": "pkg:pypi/requests@2.25.1", + "type": "library", + "bom-ref": "pkg:pypi/requests@2.25.1" + }, + { + "name": "uvicorn", + "version": "0.17.0", + "purl": "pkg:pypi/uvicorn@0.17.0", + "type": "library", + "bom-ref": "pkg:pypi/uvicorn@0.17.0" + } + ], + "dependencies": [ + { + "ref": "pkg:pypi/test-project@0.1.0", + "dependsOn": [ + "pkg:pypi/beautifulsoup4@4.12.2", + "pkg:pypi/certifi@2023.7.22", + "pkg:pypi/click@8.0.4", + "pkg:pypi/flask@2.0.3", + "pkg:pypi/requests@2.25.1", + "pkg:pypi/uvicorn@0.17.0" + ] + }, + { + "ref": "pkg:pypi/beautifulsoup4@4.12.2", + "dependsOn": [] + }, + { + "ref": "pkg:pypi/certifi@2023.7.22", + "dependsOn": [] + }, + { + "ref": "pkg:pypi/click@8.0.4", + "dependsOn": [] + }, + { + "ref": "pkg:pypi/flask@2.0.3", + "dependsOn": [] + }, + { + "ref": "pkg:pypi/requests@2.25.1", + "dependsOn": [] + }, + { + "ref": "pkg:pypi/uvicorn@0.17.0", + "dependsOn": [] + } + ] } diff --git a/test/providers/tst_manifests/pyproject/uv_self_ref/expected_stack_sbom.json b/test/providers/tst_manifests/pyproject/uv_self_ref/expected_stack_sbom.json index 464d5e13..910748da 100644 --- a/test/providers/tst_manifests/pyproject/uv_self_ref/expected_stack_sbom.json +++ b/test/providers/tst_manifests/pyproject/uv_self_ref/expected_stack_sbom.json @@ -1,239 +1,226 @@ { - "bomFormat": "CycloneDX", - "specVersion": "1.4", - "version": 1, - "metadata": { - "timestamp": "2023-10-01T00:00:00.000Z", - "component": { - "name": "test-project", - "version": "0.1.0", - "purl": "pkg:pypi/test-project@0.1.0", - "type": "application", - "bom-ref": "pkg:pypi/test-project@0.1.0" - } - }, - "components": [ - { - "name": "beautifulsoup4", - "version": "4.12.2", - "purl": "pkg:pypi/beautifulsoup4@4.12.2", - "type": "library", - "bom-ref": "pkg:pypi/beautifulsoup4@4.12.2" - }, - { - "name": "certifi", - "version": "2023.7.22", - "purl": "pkg:pypi/certifi@2023.7.22", - "type": "library", - "bom-ref": "pkg:pypi/certifi@2023.7.22" - }, - { - "name": "click", - "version": "8.0.4", - "purl": "pkg:pypi/click@8.0.4", - "type": "library", - "bom-ref": "pkg:pypi/click@8.0.4" - }, - { - "name": "flask", - "version": "2.0.3", - "purl": "pkg:pypi/flask@2.0.3", - "type": "library", - "bom-ref": "pkg:pypi/flask@2.0.3" - }, - { - "name": "requests", - "version": "2.25.1", - "purl": "pkg:pypi/requests@2.25.1", - "type": "library", - "bom-ref": "pkg:pypi/requests@2.25.1" - }, - { - "name": "uvicorn", - "version": "0.17.0", - "purl": "pkg:pypi/uvicorn@0.17.0", - "type": "library", - "bom-ref": "pkg:pypi/uvicorn@0.17.0" - }, - { - "name": "soupsieve", - "version": "2.8.3", - "purl": "pkg:pypi/soupsieve@2.8.3", - "type": "library", - "bom-ref": "pkg:pypi/soupsieve@2.8.3" - }, - { - "name": "colorama", - "version": "0.4.6", - "purl": "pkg:pypi/colorama@0.4.6", - "type": "library", - "bom-ref": "pkg:pypi/colorama@0.4.6" - }, - { - "name": "itsdangerous", - "version": "2.2.0", - "purl": "pkg:pypi/itsdangerous@2.2.0", - "type": "library", - "bom-ref": "pkg:pypi/itsdangerous@2.2.0" - }, - { - "name": "jinja2", - "version": "3.1.6", - "purl": "pkg:pypi/jinja2@3.1.6", - "type": "library", - "bom-ref": "pkg:pypi/jinja2@3.1.6" - }, - { - "name": "werkzeug", - "version": "3.1.8", - "purl": "pkg:pypi/werkzeug@3.1.8", - "type": "library", - "bom-ref": "pkg:pypi/werkzeug@3.1.8" - }, - { - "name": "markupsafe", - "version": "3.0.3", - "purl": "pkg:pypi/markupsafe@3.0.3", - "type": "library", - "bom-ref": "pkg:pypi/markupsafe@3.0.3" - }, - { - "name": "chardet", - "version": "4.0.0", - "purl": "pkg:pypi/chardet@4.0.0", - "type": "library", - "bom-ref": "pkg:pypi/chardet@4.0.0" - }, - { - "name": "idna", - "version": "2.10", - "purl": "pkg:pypi/idna@2.10", - "type": "library", - "bom-ref": "pkg:pypi/idna@2.10" - }, - { - "name": "urllib3", - "version": "1.26.20", - "purl": "pkg:pypi/urllib3@1.26.20", - "type": "library", - "bom-ref": "pkg:pypi/urllib3@1.26.20" - }, - { - "name": "asgiref", - "version": "3.11.1", - "purl": "pkg:pypi/asgiref@3.11.1", - "type": "library", - "bom-ref": "pkg:pypi/asgiref@3.11.1" - }, - { - "name": "h11", - "version": "0.16.0", - "purl": "pkg:pypi/h11@0.16.0", - "type": "library", - "bom-ref": "pkg:pypi/h11@0.16.0" - } - ], - "dependencies": [ - { - "ref": "pkg:pypi/test-project@0.1.0", - "dependsOn": [ - "pkg:pypi/beautifulsoup4@4.12.2", - "pkg:pypi/certifi@2023.7.22", - "pkg:pypi/click@8.0.4", - "pkg:pypi/flask@2.0.3", - "pkg:pypi/requests@2.25.1", - "pkg:pypi/uvicorn@0.17.0" - ] - }, - { - "ref": "pkg:pypi/beautifulsoup4@4.12.2", - "dependsOn": [ - "pkg:pypi/soupsieve@2.8.3" - ] - }, - { - "ref": "pkg:pypi/certifi@2023.7.22", - "dependsOn": [] - }, - { - "ref": "pkg:pypi/click@8.0.4", - "dependsOn": [ - "pkg:pypi/colorama@0.4.6" - ] - }, - { - "ref": "pkg:pypi/flask@2.0.3", - "dependsOn": [ - "pkg:pypi/click@8.0.4", - "pkg:pypi/itsdangerous@2.2.0", - "pkg:pypi/jinja2@3.1.6", - "pkg:pypi/werkzeug@3.1.8" - ] - }, - { - "ref": "pkg:pypi/requests@2.25.1", - "dependsOn": [ - "pkg:pypi/certifi@2023.7.22", - "pkg:pypi/chardet@4.0.0", - "pkg:pypi/idna@2.10", - "pkg:pypi/urllib3@1.26.20" - ] - }, - { - "ref": "pkg:pypi/uvicorn@0.17.0", - "dependsOn": [ - "pkg:pypi/asgiref@3.11.1", - "pkg:pypi/click@8.0.4", - "pkg:pypi/h11@0.16.0" - ] - }, - { - "ref": "pkg:pypi/soupsieve@2.8.3", - "dependsOn": [] - }, - { - "ref": "pkg:pypi/colorama@0.4.6", - "dependsOn": [] - }, - { - "ref": "pkg:pypi/itsdangerous@2.2.0", - "dependsOn": [] - }, - { - "ref": "pkg:pypi/jinja2@3.1.6", - "dependsOn": [ - "pkg:pypi/markupsafe@3.0.3" - ] - }, - { - "ref": "pkg:pypi/werkzeug@3.1.8", - "dependsOn": [ - "pkg:pypi/markupsafe@3.0.3" - ] - }, - { - "ref": "pkg:pypi/markupsafe@3.0.3", - "dependsOn": [] - }, - { - "ref": "pkg:pypi/chardet@4.0.0", - "dependsOn": [] - }, - { - "ref": "pkg:pypi/idna@2.10", - "dependsOn": [] - }, - { - "ref": "pkg:pypi/urllib3@1.26.20", - "dependsOn": [] - }, - { - "ref": "pkg:pypi/asgiref@3.11.1", - "dependsOn": [] - }, - { - "ref": "pkg:pypi/h11@0.16.0", - "dependsOn": [] - } - ] + "bomFormat": "CycloneDX", + "specVersion": "1.4", + "version": 1, + "metadata": { + "timestamp": "2023-10-01T00:00:00.000Z", + "component": { + "name": "test-project", + "version": "0.1.0", + "purl": "pkg:pypi/test-project@0.1.0", + "type": "application", + "bom-ref": "pkg:pypi/test-project@0.1.0" + } + }, + "components": [ + { + "name": "beautifulsoup4", + "version": "4.12.2", + "purl": "pkg:pypi/beautifulsoup4@4.12.2", + "type": "library", + "bom-ref": "pkg:pypi/beautifulsoup4@4.12.2" + }, + { + "name": "certifi", + "version": "2023.7.22", + "purl": "pkg:pypi/certifi@2023.7.22", + "type": "library", + "bom-ref": "pkg:pypi/certifi@2023.7.22" + }, + { + "name": "click", + "version": "8.0.4", + "purl": "pkg:pypi/click@8.0.4", + "type": "library", + "bom-ref": "pkg:pypi/click@8.0.4" + }, + { + "name": "flask", + "version": "2.0.3", + "purl": "pkg:pypi/flask@2.0.3", + "type": "library", + "bom-ref": "pkg:pypi/flask@2.0.3" + }, + { + "name": "requests", + "version": "2.25.1", + "purl": "pkg:pypi/requests@2.25.1", + "type": "library", + "bom-ref": "pkg:pypi/requests@2.25.1" + }, + { + "name": "uvicorn", + "version": "0.17.0", + "purl": "pkg:pypi/uvicorn@0.17.0", + "type": "library", + "bom-ref": "pkg:pypi/uvicorn@0.17.0" + }, + { + "name": "soupsieve", + "version": "2.8.3", + "purl": "pkg:pypi/soupsieve@2.8.3", + "type": "library", + "bom-ref": "pkg:pypi/soupsieve@2.8.3" + }, + { + "name": "itsdangerous", + "version": "2.2.0", + "purl": "pkg:pypi/itsdangerous@2.2.0", + "type": "library", + "bom-ref": "pkg:pypi/itsdangerous@2.2.0" + }, + { + "name": "jinja2", + "version": "3.1.6", + "purl": "pkg:pypi/jinja2@3.1.6", + "type": "library", + "bom-ref": "pkg:pypi/jinja2@3.1.6" + }, + { + "name": "werkzeug", + "version": "3.1.8", + "purl": "pkg:pypi/werkzeug@3.1.8", + "type": "library", + "bom-ref": "pkg:pypi/werkzeug@3.1.8" + }, + { + "name": "markupsafe", + "version": "3.0.3", + "purl": "pkg:pypi/markupsafe@3.0.3", + "type": "library", + "bom-ref": "pkg:pypi/markupsafe@3.0.3" + }, + { + "name": "chardet", + "version": "4.0.0", + "purl": "pkg:pypi/chardet@4.0.0", + "type": "library", + "bom-ref": "pkg:pypi/chardet@4.0.0" + }, + { + "name": "idna", + "version": "2.10", + "purl": "pkg:pypi/idna@2.10", + "type": "library", + "bom-ref": "pkg:pypi/idna@2.10" + }, + { + "name": "urllib3", + "version": "1.26.20", + "purl": "pkg:pypi/urllib3@1.26.20", + "type": "library", + "bom-ref": "pkg:pypi/urllib3@1.26.20" + }, + { + "name": "asgiref", + "version": "3.11.1", + "purl": "pkg:pypi/asgiref@3.11.1", + "type": "library", + "bom-ref": "pkg:pypi/asgiref@3.11.1" + }, + { + "name": "h11", + "version": "0.16.0", + "purl": "pkg:pypi/h11@0.16.0", + "type": "library", + "bom-ref": "pkg:pypi/h11@0.16.0" + } + ], + "dependencies": [ + { + "ref": "pkg:pypi/test-project@0.1.0", + "dependsOn": [ + "pkg:pypi/beautifulsoup4@4.12.2", + "pkg:pypi/certifi@2023.7.22", + "pkg:pypi/click@8.0.4", + "pkg:pypi/flask@2.0.3", + "pkg:pypi/requests@2.25.1", + "pkg:pypi/uvicorn@0.17.0" + ] + }, + { + "ref": "pkg:pypi/beautifulsoup4@4.12.2", + "dependsOn": [ + "pkg:pypi/soupsieve@2.8.3" + ] + }, + { + "ref": "pkg:pypi/certifi@2023.7.22", + "dependsOn": [] + }, + { + "ref": "pkg:pypi/click@8.0.4", + "dependsOn": [] + }, + { + "ref": "pkg:pypi/flask@2.0.3", + "dependsOn": [ + "pkg:pypi/click@8.0.4", + "pkg:pypi/itsdangerous@2.2.0", + "pkg:pypi/jinja2@3.1.6", + "pkg:pypi/werkzeug@3.1.8" + ] + }, + { + "ref": "pkg:pypi/requests@2.25.1", + "dependsOn": [ + "pkg:pypi/certifi@2023.7.22", + "pkg:pypi/chardet@4.0.0", + "pkg:pypi/idna@2.10", + "pkg:pypi/urllib3@1.26.20" + ] + }, + { + "ref": "pkg:pypi/uvicorn@0.17.0", + "dependsOn": [ + "pkg:pypi/asgiref@3.11.1", + "pkg:pypi/click@8.0.4", + "pkg:pypi/h11@0.16.0" + ] + }, + { + "ref": "pkg:pypi/soupsieve@2.8.3", + "dependsOn": [] + }, + { + "ref": "pkg:pypi/itsdangerous@2.2.0", + "dependsOn": [] + }, + { + "ref": "pkg:pypi/jinja2@3.1.6", + "dependsOn": [ + "pkg:pypi/markupsafe@3.0.3" + ] + }, + { + "ref": "pkg:pypi/werkzeug@3.1.8", + "dependsOn": [ + "pkg:pypi/markupsafe@3.0.3" + ] + }, + { + "ref": "pkg:pypi/markupsafe@3.0.3", + "dependsOn": [] + }, + { + "ref": "pkg:pypi/chardet@4.0.0", + "dependsOn": [] + }, + { + "ref": "pkg:pypi/idna@2.10", + "dependsOn": [] + }, + { + "ref": "pkg:pypi/urllib3@1.26.20", + "dependsOn": [] + }, + { + "ref": "pkg:pypi/asgiref@3.11.1", + "dependsOn": [] + }, + { + "ref": "pkg:pypi/h11@0.16.0", + "dependsOn": [] + } + ] } diff --git a/test/providers/tst_manifests/pyproject/uv_workspace/expected_component_sbom.json b/test/providers/tst_manifests/pyproject/uv_workspace/expected_component_sbom.json index 625332dc..5c56a20c 100644 --- a/test/providers/tst_manifests/pyproject/uv_workspace/expected_component_sbom.json +++ b/test/providers/tst_manifests/pyproject/uv_workspace/expected_component_sbom.json @@ -1,48 +1,48 @@ { - "bomFormat": "CycloneDX", - "specVersion": "1.4", - "version": 1, - "metadata": { - "timestamp": "2023-10-01T00:00:00.000Z", - "component": { - "name": "uv-mono", - "version": "0.1.0", - "purl": "pkg:pypi/uv-mono@0.1.0", - "type": "application", - "bom-ref": "pkg:pypi/uv-mono@0.1.0" - } - }, - "components": [ - { - "name": "mid-pkg", - "version": "0.1.0", - "purl": "pkg:pypi/mid-pkg@0.1.0", - "type": "library", - "bom-ref": "pkg:pypi/mid-pkg@0.1.0" - }, - { - "name": "flask", - "version": "2.0.3", - "purl": "pkg:pypi/flask@2.0.3", - "type": "library", - "bom-ref": "pkg:pypi/flask@2.0.3" - } - ], - "dependencies": [ - { - "ref": "pkg:pypi/uv-mono@0.1.0", - "dependsOn": [ - "pkg:pypi/mid-pkg@0.1.0", - "pkg:pypi/flask@2.0.3" - ] - }, - { - "ref": "pkg:pypi/mid-pkg@0.1.0", - "dependsOn": [] - }, - { - "ref": "pkg:pypi/flask@2.0.3", - "dependsOn": [] - } - ] + "bomFormat": "CycloneDX", + "specVersion": "1.4", + "version": 1, + "metadata": { + "timestamp": "2023-10-01T00:00:00.000Z", + "component": { + "name": "uv-mono", + "version": "0.1.0", + "purl": "pkg:pypi/uv-mono@0.1.0", + "type": "application", + "bom-ref": "pkg:pypi/uv-mono@0.1.0" + } + }, + "components": [ + { + "name": "mid-pkg", + "version": "0.1.0", + "purl": "pkg:pypi/mid-pkg@0.1.0", + "type": "library", + "bom-ref": "pkg:pypi/mid-pkg@0.1.0" + }, + { + "name": "flask", + "version": "2.0.3", + "purl": "pkg:pypi/flask@2.0.3", + "type": "library", + "bom-ref": "pkg:pypi/flask@2.0.3" + } + ], + "dependencies": [ + { + "ref": "pkg:pypi/uv-mono@0.1.0", + "dependsOn": [ + "pkg:pypi/mid-pkg@0.1.0", + "pkg:pypi/flask@2.0.3" + ] + }, + { + "ref": "pkg:pypi/mid-pkg@0.1.0", + "dependsOn": [] + }, + { + "ref": "pkg:pypi/flask@2.0.3", + "dependsOn": [] + } + ] } diff --git a/test/providers/tst_manifests/pyproject/uv_workspace/expected_stack_sbom.json b/test/providers/tst_manifests/pyproject/uv_workspace/expected_stack_sbom.json index c4b77af6..273778b7 100644 --- a/test/providers/tst_manifests/pyproject/uv_workspace/expected_stack_sbom.json +++ b/test/providers/tst_manifests/pyproject/uv_workspace/expected_stack_sbom.json @@ -1,200 +1,187 @@ { - "bomFormat": "CycloneDX", - "specVersion": "1.4", - "version": 1, - "metadata": { - "timestamp": "2023-10-01T00:00:00.000Z", - "component": { - "name": "uv-mono", - "version": "0.1.0", - "purl": "pkg:pypi/uv-mono@0.1.0", - "type": "application", - "bom-ref": "pkg:pypi/uv-mono@0.1.0" - } - }, - "components": [ - { - "name": "mid-pkg", - "version": "0.1.0", - "purl": "pkg:pypi/mid-pkg@0.1.0", - "type": "library", - "bom-ref": "pkg:pypi/mid-pkg@0.1.0" - }, - { - "name": "flask", - "version": "2.0.3", - "purl": "pkg:pypi/flask@2.0.3", - "type": "library", - "bom-ref": "pkg:pypi/flask@2.0.3" - }, - { - "name": "sub-pkg", - "version": "0.1.0", - "purl": "pkg:pypi/sub-pkg@0.1.0", - "type": "library", - "bom-ref": "pkg:pypi/sub-pkg@0.1.0" - }, - { - "name": "requests", - "version": "2.33.1", - "purl": "pkg:pypi/requests@2.33.1", - "type": "library", - "bom-ref": "pkg:pypi/requests@2.33.1" - }, - { - "name": "click", - "version": "8.3.2", - "purl": "pkg:pypi/click@8.3.2", - "type": "library", - "bom-ref": "pkg:pypi/click@8.3.2" - }, - { - "name": "colorama", - "version": "0.4.6", - "purl": "pkg:pypi/colorama@0.4.6", - "type": "library", - "bom-ref": "pkg:pypi/colorama@0.4.6" - }, - { - "name": "itsdangerous", - "version": "2.2.0", - "purl": "pkg:pypi/itsdangerous@2.2.0", - "type": "library", - "bom-ref": "pkg:pypi/itsdangerous@2.2.0" - }, - { - "name": "jinja2", - "version": "3.1.6", - "purl": "pkg:pypi/jinja2@3.1.6", - "type": "library", - "bom-ref": "pkg:pypi/jinja2@3.1.6" - }, - { - "name": "werkzeug", - "version": "3.1.8", - "purl": "pkg:pypi/werkzeug@3.1.8", - "type": "library", - "bom-ref": "pkg:pypi/werkzeug@3.1.8" - }, - { - "name": "markupsafe", - "version": "3.0.3", - "purl": "pkg:pypi/markupsafe@3.0.3", - "type": "library", - "bom-ref": "pkg:pypi/markupsafe@3.0.3" - }, - { - "name": "certifi", - "version": "2026.2.25", - "purl": "pkg:pypi/certifi@2026.2.25", - "type": "library", - "bom-ref": "pkg:pypi/certifi@2026.2.25" - }, - { - "name": "charset-normalizer", - "version": "3.4.7", - "purl": "pkg:pypi/charset-normalizer@3.4.7", - "type": "library", - "bom-ref": "pkg:pypi/charset-normalizer@3.4.7" - }, - { - "name": "idna", - "version": "3.11", - "purl": "pkg:pypi/idna@3.11", - "type": "library", - "bom-ref": "pkg:pypi/idna@3.11" - }, - { - "name": "urllib3", - "version": "2.6.3", - "purl": "pkg:pypi/urllib3@2.6.3", - "type": "library", - "bom-ref": "pkg:pypi/urllib3@2.6.3" - } - ], - "dependencies": [ - { - "ref": "pkg:pypi/uv-mono@0.1.0", - "dependsOn": [ - "pkg:pypi/mid-pkg@0.1.0", - "pkg:pypi/flask@2.0.3" - ] - }, - { - "ref": "pkg:pypi/mid-pkg@0.1.0", - "dependsOn": [ - "pkg:pypi/sub-pkg@0.1.0" - ] - }, - { - "ref": "pkg:pypi/flask@2.0.3", - "dependsOn": [ - "pkg:pypi/click@8.3.2", - "pkg:pypi/itsdangerous@2.2.0", - "pkg:pypi/jinja2@3.1.6", - "pkg:pypi/werkzeug@3.1.8" - ] - }, - { - "ref": "pkg:pypi/sub-pkg@0.1.0", - "dependsOn": [ - "pkg:pypi/requests@2.33.1" - ] - }, - { - "ref": "pkg:pypi/requests@2.33.1", - "dependsOn": [ - "pkg:pypi/certifi@2026.2.25", - "pkg:pypi/charset-normalizer@3.4.7", - "pkg:pypi/idna@3.11", - "pkg:pypi/urllib3@2.6.3" - ] - }, - { - "ref": "pkg:pypi/click@8.3.2", - "dependsOn": [ - "pkg:pypi/colorama@0.4.6" - ] - }, - { - "ref": "pkg:pypi/colorama@0.4.6", - "dependsOn": [] - }, - { - "ref": "pkg:pypi/itsdangerous@2.2.0", - "dependsOn": [] - }, - { - "ref": "pkg:pypi/jinja2@3.1.6", - "dependsOn": [ - "pkg:pypi/markupsafe@3.0.3" - ] - }, - { - "ref": "pkg:pypi/werkzeug@3.1.8", - "dependsOn": [ - "pkg:pypi/markupsafe@3.0.3" - ] - }, - { - "ref": "pkg:pypi/markupsafe@3.0.3", - "dependsOn": [] - }, - { - "ref": "pkg:pypi/certifi@2026.2.25", - "dependsOn": [] - }, - { - "ref": "pkg:pypi/charset-normalizer@3.4.7", - "dependsOn": [] - }, - { - "ref": "pkg:pypi/idna@3.11", - "dependsOn": [] - }, - { - "ref": "pkg:pypi/urllib3@2.6.3", - "dependsOn": [] - } - ] + "bomFormat": "CycloneDX", + "specVersion": "1.4", + "version": 1, + "metadata": { + "timestamp": "2023-10-01T00:00:00.000Z", + "component": { + "name": "uv-mono", + "version": "0.1.0", + "purl": "pkg:pypi/uv-mono@0.1.0", + "type": "application", + "bom-ref": "pkg:pypi/uv-mono@0.1.0" + } + }, + "components": [ + { + "name": "mid-pkg", + "version": "0.1.0", + "purl": "pkg:pypi/mid-pkg@0.1.0", + "type": "library", + "bom-ref": "pkg:pypi/mid-pkg@0.1.0" + }, + { + "name": "flask", + "version": "2.0.3", + "purl": "pkg:pypi/flask@2.0.3", + "type": "library", + "bom-ref": "pkg:pypi/flask@2.0.3" + }, + { + "name": "sub-pkg", + "version": "0.1.0", + "purl": "pkg:pypi/sub-pkg@0.1.0", + "type": "library", + "bom-ref": "pkg:pypi/sub-pkg@0.1.0" + }, + { + "name": "requests", + "version": "2.33.1", + "purl": "pkg:pypi/requests@2.33.1", + "type": "library", + "bom-ref": "pkg:pypi/requests@2.33.1" + }, + { + "name": "click", + "version": "8.3.2", + "purl": "pkg:pypi/click@8.3.2", + "type": "library", + "bom-ref": "pkg:pypi/click@8.3.2" + }, + { + "name": "itsdangerous", + "version": "2.2.0", + "purl": "pkg:pypi/itsdangerous@2.2.0", + "type": "library", + "bom-ref": "pkg:pypi/itsdangerous@2.2.0" + }, + { + "name": "jinja2", + "version": "3.1.6", + "purl": "pkg:pypi/jinja2@3.1.6", + "type": "library", + "bom-ref": "pkg:pypi/jinja2@3.1.6" + }, + { + "name": "werkzeug", + "version": "3.1.8", + "purl": "pkg:pypi/werkzeug@3.1.8", + "type": "library", + "bom-ref": "pkg:pypi/werkzeug@3.1.8" + }, + { + "name": "markupsafe", + "version": "3.0.3", + "purl": "pkg:pypi/markupsafe@3.0.3", + "type": "library", + "bom-ref": "pkg:pypi/markupsafe@3.0.3" + }, + { + "name": "certifi", + "version": "2026.2.25", + "purl": "pkg:pypi/certifi@2026.2.25", + "type": "library", + "bom-ref": "pkg:pypi/certifi@2026.2.25" + }, + { + "name": "charset-normalizer", + "version": "3.4.7", + "purl": "pkg:pypi/charset-normalizer@3.4.7", + "type": "library", + "bom-ref": "pkg:pypi/charset-normalizer@3.4.7" + }, + { + "name": "idna", + "version": "3.11", + "purl": "pkg:pypi/idna@3.11", + "type": "library", + "bom-ref": "pkg:pypi/idna@3.11" + }, + { + "name": "urllib3", + "version": "2.6.3", + "purl": "pkg:pypi/urllib3@2.6.3", + "type": "library", + "bom-ref": "pkg:pypi/urllib3@2.6.3" + } + ], + "dependencies": [ + { + "ref": "pkg:pypi/uv-mono@0.1.0", + "dependsOn": [ + "pkg:pypi/mid-pkg@0.1.0", + "pkg:pypi/flask@2.0.3" + ] + }, + { + "ref": "pkg:pypi/mid-pkg@0.1.0", + "dependsOn": [ + "pkg:pypi/sub-pkg@0.1.0" + ] + }, + { + "ref": "pkg:pypi/flask@2.0.3", + "dependsOn": [ + "pkg:pypi/click@8.3.2", + "pkg:pypi/itsdangerous@2.2.0", + "pkg:pypi/jinja2@3.1.6", + "pkg:pypi/werkzeug@3.1.8" + ] + }, + { + "ref": "pkg:pypi/sub-pkg@0.1.0", + "dependsOn": [ + "pkg:pypi/requests@2.33.1" + ] + }, + { + "ref": "pkg:pypi/requests@2.33.1", + "dependsOn": [ + "pkg:pypi/certifi@2026.2.25", + "pkg:pypi/charset-normalizer@3.4.7", + "pkg:pypi/idna@3.11", + "pkg:pypi/urllib3@2.6.3" + ] + }, + { + "ref": "pkg:pypi/click@8.3.2", + "dependsOn": [] + }, + { + "ref": "pkg:pypi/itsdangerous@2.2.0", + "dependsOn": [] + }, + { + "ref": "pkg:pypi/jinja2@3.1.6", + "dependsOn": [ + "pkg:pypi/markupsafe@3.0.3" + ] + }, + { + "ref": "pkg:pypi/werkzeug@3.1.8", + "dependsOn": [ + "pkg:pypi/markupsafe@3.0.3" + ] + }, + { + "ref": "pkg:pypi/markupsafe@3.0.3", + "dependsOn": [] + }, + { + "ref": "pkg:pypi/certifi@2026.2.25", + "dependsOn": [] + }, + { + "ref": "pkg:pypi/charset-normalizer@3.4.7", + "dependsOn": [] + }, + { + "ref": "pkg:pypi/idna@3.11", + "dependsOn": [] + }, + { + "ref": "pkg:pypi/urllib3@2.6.3", + "dependsOn": [] + } + ] }