Inventory all four pinned Python runtime/build roots and 20 resolved artifacts with hashes and environment evidence. Reject stale reports; retain custom license names and exact per-file obligations. Trace all eleven prior missing declarations to exact artifacts, registry metadata or parent POMs. Record the newly exposed high ecdsa advisory without suppression and refresh integrated input/BOM hashes while retaining prior receipts. Four pure inventory regressions pass; no host install, release build or live node operation.
91 lines
4.7 KiB
Python
91 lines
4.7 KiB
Python
"""Pure inventory fixtures: no application imports, network, installs or builds."""
|
|
import json
|
|
from pathlib import Path
|
|
import subprocess
|
|
import sys
|
|
import tempfile
|
|
import unittest
|
|
|
|
|
|
SCRIPT = Path(__file__).resolve().parents[1] / 'security-dependency-inventory.py'
|
|
|
|
|
|
class PythonInventoryTests(unittest.TestCase):
|
|
def setUp(self):
|
|
self.tmp = tempfile.TemporaryDirectory()
|
|
self.addCleanup(self.tmp.cleanup)
|
|
self.root = Path(self.tmp.name)
|
|
for path in ['core/Cargo.lock', 'Android/rust/archy-fips-core/Cargo.lock']:
|
|
self.write(path, 'version = 3\npackage = []\n')
|
|
for path in ['neode-ui/package-lock.json', 'apps/router/package-lock.json',
|
|
'examples/archipelago-app-starter/package-lock.json']:
|
|
self.write(path, '{"packages": {}}')
|
|
for path in ['aiui/pnpm-lock.yaml', 'docker/gashboard/pnpm-lock.yaml']:
|
|
self.write(path, 'packages: {}\n')
|
|
self.write('reticulum-daemon/requirements.txt', 'rns==1.3.5\nlxmf==1.0.1\n')
|
|
self.write('reticulum-daemon/requirements-build.txt', 'pyinstaller==6.21.0\n')
|
|
for path in ['reticulum-daemon/build.sh', 'reticulum-daemon/build-esptool.sh']:
|
|
self.write(path, ".venv/bin/pip install 'esptool==4.8.1'\n")
|
|
self.report = {'version': '1', 'environment': {'python_version': '3.13', 'sys_platform': 'linux'},
|
|
'install': []}
|
|
for name, version in [('rns', '1.3.5'), ('lxmf', '1.0.1'), ('pyinstaller', '6.21.0'),
|
|
('esptool', '4.8.1'), ('PyYAML', '6.0.3')]:
|
|
self.report['install'].append({
|
|
'metadata': {'name': name, 'version': version, 'license': 'Reticulum License' if name == 'rns' else 'MIT'},
|
|
'requested': name != 'PyYAML',
|
|
'download_info': {'url': 'https://example.invalid/' + name + '.whl',
|
|
'archive_info': {'hashes': {'sha256': 'a' * 64}}},
|
|
})
|
|
|
|
def write(self, path, content):
|
|
target = self.root / path
|
|
target.parent.mkdir(parents=True, exist_ok=True)
|
|
target.write_text(content)
|
|
|
|
def run_inventory(self, with_report=True):
|
|
command = [sys.executable, str(SCRIPT), '--repo', str(self.root), '--output', str(self.root / 'out')]
|
|
if with_report:
|
|
self.write('pip-report.json', json.dumps(self.report))
|
|
command += ['--python-report', str(self.root / 'pip-report.json')]
|
|
return subprocess.run(command, capture_output=True, text=True)
|
|
|
|
def test_exact_transitives_hashes_and_custom_license_names(self):
|
|
result = self.run_inventory()
|
|
self.assertEqual(result.returncode, 0, result.stderr)
|
|
bom = json.loads((self.root / 'out/source-bom.cdx.json').read_text())
|
|
components = {item['purl']: item for item in bom['components']}
|
|
self.assertEqual(len(components), 5)
|
|
self.assertIn('pkg:pypi/pyyaml@6.0.3', components)
|
|
rns = components['pkg:pypi/rns@1.3.5']
|
|
self.assertEqual(rns['licenses'], [{'license': {'name': 'Reticulum License'}}])
|
|
self.assertEqual(rns['hashes'], [{'alg': 'SHA-256', 'content': 'a' * 64}])
|
|
summary = json.loads((self.root / 'out/source-inventory.json').read_text())
|
|
self.assertEqual(summary['python_resolution']['components'], 5)
|
|
self.assertIn('esptool==4.8.1', summary['python_resolution']['roots'])
|
|
self.assertTrue(any('other-platform graph' in entry['reason'] for entry in summary['unresolved']))
|
|
|
|
def test_stale_report_rejected_after_build_pin_changes(self):
|
|
self.write('reticulum-daemon/build-esptool.sh', "pip install 'esptool==5.0.0'\n")
|
|
result = self.run_inventory()
|
|
self.assertNotEqual(result.returncode, 0)
|
|
self.assertIn('does not match reticulum-daemon/build-esptool.sh', result.stderr)
|
|
self.assertFalse((self.root / 'out/source-bom.cdx.json').exists())
|
|
|
|
def test_missing_artifact_hash_is_not_exact_evidence(self):
|
|
self.report['install'][0]['download_info']['archive_info']['hashes'] = {}
|
|
result = self.run_inventory()
|
|
self.assertNotEqual(result.returncode, 0)
|
|
self.assertIn('lacks exact SHA-256 artifact evidence', result.stderr)
|
|
|
|
def test_without_report_build_only_root_remains_unresolved(self):
|
|
result = self.run_inventory(with_report=False)
|
|
self.assertEqual(result.returncode, 0, result.stderr)
|
|
summary = json.loads((self.root / 'out/source-inventory.json').read_text())
|
|
esptool = [x for x in summary['unresolved'] if x.get('constraint') == 'esptool==4.8.1']
|
|
self.assertEqual({x['input'] for x in esptool},
|
|
{'reticulum-daemon/build.sh', 'reticulum-daemon/build-esptool.sh'})
|
|
|
|
|
|
if __name__ == '__main__':
|
|
unittest.main()
|