-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add script to check newly added packages against buildinfo files
buildinfo files contain package checksums in a machine-readable format, so script checking newly added packages against those. This will be added to CI for securedrop-apt-test and securedrop-apt-prod. The main iffy part of this is how it compares against "origin/main", but I think for PRs it'll mostly do the right thing. We only check new packages because old ones don't have buildinfo published. Maybe once we no longer have any legacy cases left, we just check everything in the repository. Likely there are more checks that could be added, but this is a start. Refs <freedomofpress/securedrop#6356>.
- Loading branch information
Showing
1 changed file
with
89 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
#!/usr/bin/env python3 | ||
""" | ||
Check newly added packages against their buildinfo files | ||
Example: | ||
./check-buildinfo package.deb package.buildinfo | ||
""" | ||
import argparse | ||
import hashlib | ||
import subprocess | ||
import sys | ||
from pathlib import Path | ||
from typing import List | ||
|
||
from debian.deb822 import BuildInfo | ||
|
||
|
||
def lookup_buildinfos(buildinfos: Path) -> dict: | ||
"""Extract checksums out of every buildinfo file we can find""" | ||
data = {} | ||
for path in buildinfos.glob("**/*.buildinfo"): | ||
info = BuildInfo(path.read_text()) | ||
for details in info['Checksums-Sha256']: | ||
if details['name'].endswith('.deb'): | ||
data[details['name']] = details['sha256'] | ||
return data | ||
|
||
|
||
def check_package(package: Path, buildinfos: dict) -> bool: | ||
"""Verify the package's checksum matches buildinfo""" | ||
try: | ||
expected = buildinfos[str(package.name)] | ||
except IndexError: | ||
print(f"ERROR: Unable to find buildinfo containing {package.name}") | ||
return False | ||
actual = hashlib.sha256(package.read_bytes()).hexdigest() | ||
if actual == expected: | ||
print(f"OK: got expected checksum {actual} for {package.name}") | ||
return True | ||
else: | ||
print(f"ERROR: package is {actual}, buildinfo has {expected} for {package.name}") | ||
return False | ||
|
||
|
||
def added_files(against="origin/main") -> List[Path]: | ||
"""Get list of added files compared to main""" | ||
added = [] | ||
output = subprocess.check_output([ | ||
"git", "log", | ||
# Only list added files | ||
"--diff-filter=A", | ||
# Set our terminal width to be huge so it doesn't truncate | ||
"--stat=999999", | ||
# Output nothing else | ||
"--pretty=", | ||
f"{against}..HEAD" | ||
], text=True) | ||
for line in output.splitlines(): | ||
if "|" not in line: | ||
continue | ||
path = Path(line.split("|", 1)[0].strip()) | ||
if path.exists(): | ||
# Wasn't deleted in an intermediate commit | ||
added.append(path) | ||
added.sort(key=lambda x: x.name) | ||
return added | ||
|
||
|
||
def main(): | ||
parser = argparse.ArgumentParser( | ||
description="Check packages against their buildinfo files" | ||
) | ||
parser.add_argument("buildinfos", type=Path, help="Folder with buildinfo files") | ||
args = parser.parse_args() | ||
buildinfos = lookup_buildinfos(args.buildinfos) | ||
status = 0 | ||
added = added_files() | ||
if not added: | ||
print("No new packages detected.") | ||
sys.exit(0) | ||
for package in added: | ||
if not check_package(package, buildinfos): | ||
status = 1 | ||
sys.exit(status) | ||
|
||
|
||
if __name__ == '__main__': | ||
main() |