initial commit
This commit is contained in:
@@ -0,0 +1,117 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Recover two explicitly declared Patent files omitted by the fixed Qt SDK SBOM."""
|
||||
import argparse
|
||||
import hashlib
|
||||
import json
|
||||
from pathlib import Path
|
||||
import re
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
BASE = ROOT / 'tests/results/source-archives/qt-sdk-notices-final/report.json'
|
||||
BASE_SHA = 'cea0a2f80ad50b5519fbad94c1dd2eed55f5ffa34e83d1360ce8da6902a845a0'
|
||||
INVENTORY_SHA = '43dc4aa3cf0d970fc97a9db2555f0025429e9bd31823cb794fd5526496d1a9e0'
|
||||
SOURCE = ROOT / '.deps/source-archives/qt-6.11.2-regular-files'
|
||||
PREFIX = 'qtwebengine/src/3rdparty/chromium/'
|
||||
PATENTS = {
|
||||
'libwebm': ('source/LICENSE.TXT', 'source/PATENTS.TXT'),
|
||||
'libwebp': ('src/COPYING', 'src/PATENTS'),
|
||||
}
|
||||
ID_ONLY = {
|
||||
'mini-chromium': ('third_party/crashpad/crashpad/third_party/mini_chromium/README.crashpad', 'Shipped in Chromium'),
|
||||
'libdrm': ('third_party/libdrm/README.chromium', 'Shipped'),
|
||||
}
|
||||
|
||||
|
||||
def sha(path):
|
||||
with path.open('rb') as stream:
|
||||
return hashlib.file_digest(stream, 'sha256').hexdigest()
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument('--output', type=Path, required=True)
|
||||
args = parser.parse_args()
|
||||
output = args.output.resolve()
|
||||
if not output.is_relative_to(ROOT):
|
||||
parser.error('Output must be inside the workspace')
|
||||
assert sha(BASE) == BASE_SHA
|
||||
base = json.loads(BASE.read_text())
|
||||
inventory = ROOT / 'tests/results/source-archives/qt-inspection/files.jsonl'
|
||||
assert sha(inventory) == INVENTORY_SHA
|
||||
names = {PREFIX + 'tools/licenses/sbom.py'}
|
||||
for name, files in PATENTS.items():
|
||||
names.update(PREFIX + 'third_party/' + name + '/' + tail for tail in (*files, 'README.chromium'))
|
||||
names.update(PREFIX + row[0] for row in ID_ONLY.values())
|
||||
sources = {}
|
||||
for line in inventory.open():
|
||||
row = json.loads(line)
|
||||
if row['path'] in names:
|
||||
assert row['path'] not in sources
|
||||
source = SOURCE / row['path']
|
||||
assert source.is_file() and not source.is_symlink()
|
||||
assert source.stat().st_size == row['bytes'] and sha(source) == row['sha256']
|
||||
sources[row['path']] = row
|
||||
assert sources.keys() == names
|
||||
assert sources[PREFIX + 'tools/licenses/sbom.py']['sha256'] == base['generatorSources'][PREFIX + 'tools/licenses/sbom.py']
|
||||
packages = {p['SPDXID']: p for p in base['packages']}
|
||||
output.mkdir(parents=True, exist_ok=False)
|
||||
copied, notices, id_only = [], [], []
|
||||
|
||||
def copy(name, kind):
|
||||
source = sources[name]
|
||||
target = 'sources/' + name.removeprefix(PREFIX)
|
||||
path = output / target
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
path.write_bytes((SOURCE / name).read_bytes())
|
||||
assert sha(path) == source['sha256']
|
||||
copied.append(dict(file=target, source=name, sha256=source['sha256'], bytes=source['bytes'], kind=kind))
|
||||
return target
|
||||
|
||||
def fields(name):
|
||||
return dict(re.findall(r'^([^:\n]+): (.*)$', (SOURCE / name).read_text(), re.M))
|
||||
|
||||
for name, (license_file, patent_file) in PATENTS.items():
|
||||
directory = PREFIX + 'third_party/' + name + '/'
|
||||
metadata = directory + 'README.chromium'
|
||||
data = fields(metadata)
|
||||
assert data['License'] == 'BSD-3-Clause, Patent' and data['Shipped'] == 'yes'
|
||||
assert data['License File'] == license_file + ', ' + patent_file
|
||||
package_id = 'SPDXRef-Package-QtWebEngine-Chromium-WebEngine-' + name
|
||||
package = packages[package_id]
|
||||
assert package['comment'].splitlines()[0] == 'Location within source: third_party/' + name
|
||||
embedded = next(n for n in base['notices'] if package_id in n['packageIds'])
|
||||
assert embedded['source'] == directory + license_file
|
||||
assert embedded['sha256'] == sources[directory + license_file]['sha256']
|
||||
assert not any(n['source'] == directory + patent_file for n in base['notices'])
|
||||
metadata_file = copy(metadata, 'upstream-package-metadata')
|
||||
notice_file = copy(directory + patent_file, 'declared-patent-notice')
|
||||
notices.append(dict(packageId=package_id, revision=data['Revision'], licenseDeclared=data['License'],
|
||||
metadataFile=metadata_file, noticeFile=notice_file,
|
||||
existingLicenseSha256=embedded['sha256'], reason='Second declared file omitted by SDK generator Patent branch'))
|
||||
for name, (relative, shipped_field) in ID_ONLY.items():
|
||||
metadata = PREFIX + relative
|
||||
data = fields(metadata)
|
||||
assert data[shipped_field] == 'no'
|
||||
package_id = 'SPDXRef-Package-QtWebEngine-Chromium-WebEngine-' + name
|
||||
package = next(p for p in base['packagesWithoutEmbeddedNoticeText'] if p['SPDXID'] == package_id)
|
||||
assert data['License'] == package['licenseConcluded']
|
||||
id_only.append(dict(packageId=package_id, metadataFile=copy(metadata, 'upstream-package-metadata'),
|
||||
declaredShippedField=shipped_field, declaredShippedValue='no',
|
||||
licenseId=package['licenseConcluded'], noticeAdded=False,
|
||||
linkedInSdk='unverified; upstream metadata is not a build-graph proof'))
|
||||
report = dict(schemaVersion=1, success=True, collectorSha256=sha(Path(__file__)),
|
||||
baseNoticeReportSha256=BASE_SHA, sourceInventorySha256=INVENTORY_SHA,
|
||||
qtSourceArchiveSha256=base['qtSourceArchiveSha256'], sdkArchiveSha256=base['sdkArchive']['sha256'],
|
||||
runtimeComparisons=base['testedRuntimeComparisons'], files=sorted(copied, key=lambda x:x['file']),
|
||||
sourceChecks=sorted(sources.values(), key=lambda x:x['path']), notices=notices, idOnlyPackages=id_only,
|
||||
sourceSdkExternalPackageReferencesValid=base['externalPackageReferencesValid'],
|
||||
completeChromiumNotices=False, completeCorrespondingSources=False, publicReleaseApproved=False,
|
||||
scope='Two upstream-declared Patent files recovered from the fixed Qt 6.11.2 source archive. '
|
||||
'Four upstream metadata files retained. Original SBOM is unchanged. '
|
||||
'Does not resolve the SDK build graph, generator skips or external package-ID inconsistency.')
|
||||
(output / 'report.json').write_text(json.dumps(report, indent=2, sort_keys=True) + '\n')
|
||||
print(json.dumps({'success': True, 'additionalNotices': len(notices), 'metadataFiles': len(copied)-len(notices)}))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Reference in New Issue
Block a user