Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 67 additions & 0 deletions check_fips_changes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
#!/usr/bin/env python3
"""Check for FIPS protected directory changes in a range of git commits.

Exits 0 if no FIPS changes found (or --fips-override is set).
Exits 1 if FIPS changes are found without override.
"""

import argparse
import subprocess
import sys

from kt.ktlib.ciq_helpers import FIPS_PROTECTED_DIRECTORIES, check_for_fips_protected_changes


def main():
parser = argparse.ArgumentParser(description="Check for FIPS protected directory changes")
parser.add_argument("--repo", help="Path to git repository", default=".")
parser.add_argument("--base-ref", help="Base ref (exclusive start of range)", required=True)
parser.add_argument("--target-ref", help="Target ref (inclusive end of range)", required=True)
parser.add_argument("--fips-override", help="Override FIPS check abort", action="store_true")
args = parser.parse_args()

print(f"[fips-check] Checking for FIPS protected changes in {args.base_ref}..{args.target_ref}")
print(f"[fips-check] Protected directories: {', '.join(d.decode() for d in FIPS_PROTECTED_DIRECTORIES)}")

try:
fips_commits = check_for_fips_protected_changes(args.repo, args.base_ref, args.target_ref)
except RuntimeError as e:
print(f"[fips-check] ERROR: {e}", file=sys.stderr)
sys.exit(1)

if not fips_commits:
print("[fips-check] No FIPS protected changes found")
sys.exit(0)

print("\n[fips-check] ========================================")
print("[fips-check] FIPS protected changes detected")
print("[fips-check] ========================================")
print(f"[fips-check] {len(fips_commits)} commit(s) touch FIPS protected directories:\n")

for sha, dirs in fips_commits.items():
result = subprocess.run(
["git", "show", "--stat", sha.decode()],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
cwd=args.repo,
)
print(f"## Commit {sha.decode()}")
if result.returncode == 0:
print(result.stdout.decode("utf-8", "backslashreplace"))
else:
print(" (could not show commit details)")
for d in sorted(dirs):
print(f" FIPS directory: {d.decode()}")
print()

if args.fips_override:
print("[fips-check] --fips-override set, continuing despite FIPS protected changes")
sys.exit(0)

print("[fips-check] Please contact the CIQ FIPS / Security team for further instructions")
print("[fips-check] Use --fips-override to bypass this check")
sys.exit(1)


if __name__ == "__main__":
main()
96 changes: 96 additions & 0 deletions kt/ktlib/ciq_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -480,6 +480,102 @@ def read_spec_el_version(spec_lines):
return _read_spec_define(spec_lines, "el_version", r"\d+")


FIPS_PROTECTED_DIRECTORIES = [
b"arch/x86/crypto/",
b"crypto/asymmetric_keys/",
b"crypto/",
b"drivers/crypto/",
b"drivers/char/random.c",
b"include/crypto",
]


def check_for_fips_protected_changes(repo_path, start_ref, end_ref):
"""Check for changes to FIPS protected directories in a range of commits.

Iterates over commits in start_ref..end_ref and checks whether any
modified files fall under a FIPS protected directory. Uses bytestrings
throughout to avoid encoding issues with international contributor names
in git output.

Parameters:
repo_path: Path to the git repository.
start_ref: The starting ref (exclusive) for the commit range.
end_ref: The ending ref (inclusive) for the commit range.

Returns:
dict mapping commit SHA (bytes) -> set of matched FIPS directory prefixes (bytes)
for each commit that touches FIPS protected paths. Empty dict if none found.
"""
print("[fips-check] Checking for FIPS protected changes")
print(f"[fips-check] Getting SHAS {start_ref}..{end_ref}")
results = subprocess.run(
["git", "log", "--pretty=%H", f"{start_ref}..{end_ref}"],
stderr=subprocess.PIPE,
stdout=subprocess.PIPE,
cwd=repo_path,
)
if results.returncode != 0:
print(results.stderr)
raise RuntimeError(f"git log failed for range {start_ref}..{end_ref}")

num_commits = len(results.stdout.split(b"\n"))
print("[fips-check] Number of commits to check: ", num_commits)
shas_to_check = {}
commits_checked = 0

progress_interval = max(1, num_commits // 10)

print("[fips-check] Checking modifications of shas")
for sha in results.stdout.split(b"\n"):
commits_checked += 1
if commits_checked % progress_interval == 0:
print(f"[fips-check] Checked {commits_checked} of {num_commits} commits")
if sha == b"":
continue
res = subprocess.run(
Comment thread
bmastbergen marked this conversation as resolved.
["git", "show", "--name-only", "--pretty=%H %s", f"{sha.decode()}"],
stderr=subprocess.PIPE,
stdout=subprocess.PIPE,
cwd=repo_path,
)
if res.returncode != 0:
print(res)
print(res.stderr)
raise RuntimeError(f"git show failed for {sha}")

sha_hash_and_subject = b""
touched_fips_files = set()

for line in res.stdout.split(b"\n"):
if sha_hash_and_subject == b"":
sha_hash_and_subject = line
continue
if line == b"":
continue

add_to_check = False

for dir in FIPS_PROTECTED_DIRECTORIES:
if line.startswith(dir):
add_to_check = True
if dir not in touched_fips_files:
touched_fips_files.add(dir)

if add_to_check:
shas_to_check[sha_hash_and_subject.split(b" ")[0]] = touched_fips_files

if touched_fips_files:
print(f"[fips-check] Checked commit {sha} touched {len(touched_fips_files)} FIPS protected files")
for f in touched_fips_files:
print(f" - {f}")
sha_hash_and_subject = b""

print(f"[fips-check] {len(shas_to_check)} of {num_commits} commits have FIPS protected changes")

return shas_to_check


def run_cve_search(vulns_repo, kernel_repo, query) -> tuple[bool, Optional[str]]:
"""
Run the cve_search script from the vulns repo.
Expand Down
99 changes: 10 additions & 89 deletions rolling-release-update.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,10 @@

import git

from kt.ktlib.ciq_helpers import get_backport_commit_data

FIPS_PROTECTED_DIRECTORIES = [
b"arch/x86/crypto/",
b"crypto/asymmetric_keys/",
b"crypto/",
b"drivers/crypto/",
b"drivers/char/random.c",
b"include/crypto",
]
from kt.ktlib.ciq_helpers import (
check_for_fips_protected_changes,
get_backport_commit_data,
)

DEBUG = False

Expand Down Expand Up @@ -108,84 +102,6 @@ def get_branch_tag_sha_list(repo, branch, minor_version=False):
return tags, last_resf_tag


def check_for_fips_protected_changes(repo, branch, common_tag):
print("[rolling release update] Checking for FIPS protected changes")
repo.git.checkout(branch)
print(f"[rolling release update] Getting SHAS {common_tag.decode()}..HEAD")
results = subprocess.run(
["git", "log", "--pretty=%H", f"{common_tag.decode()}..HEAD"],
stderr=subprocess.PIPE,
stdout=subprocess.PIPE,
cwd=repo.working_dir,
)
if results.returncode != 0:
print(results.stderr)
exit(1)

num_commits = len(results.stdout.split(b"\n"))
print("[rolling release update] Number of commits to check: ", num_commits)
shas_to_check = {}
commits_checked = 0

progress_interval = max(1, num_commits // 10)

print("[rolling release update] Checking modifications of shas")
if DEBUG:
print(results.stdout.split(b"\n"))
for sha in results.stdout.split(b"\n"):
commits_checked += 1
if commits_checked % progress_interval == 0:
print(f"[rolling release update] Checked {commits_checked} of {num_commits} commits")
if sha == b"":
continue
res = subprocess.run(
["git", "show", "--name-only", "--pretty=%H %s", f"{sha.decode()}"],
stderr=subprocess.PIPE,
stdout=subprocess.PIPE,
cwd=repo.working_dir,
)
if res.returncode != 0:
print(res)
print(res.stderr)
exit(1)

sha_hash_and_subject = b""
touched_fips_files = set()

for line in res.stdout.split(b"\n"):
if sha_hash_and_subject == b"":
sha_hash_and_subject = line
continue
if line == b"":
continue

add_to_check = False

for dir in FIPS_PROTECTED_DIRECTORIES:
if line.startswith(dir):
if DEBUG:
print(f"FIPS protected directory {dir} change found in commit {sha}")
print(sha_hash_and_subject)
add_to_check = True
if dir not in touched_fips_files:
touched_fips_files.add(dir)

if add_to_check:
shas_to_check[sha_hash_and_subject.split(b" ")[0]] = touched_fips_files

if touched_fips_files:
print(
f"[rolling release update] Checked commit {sha} touched {len(touched_fips_files)} FIPS protected files"
)
for f in touched_fips_files:
print(f" - {f}")
sha_hash_and_subject = b""

print(f"[rolling release update] {len(shas_to_check)} of {num_commits} commits have FIPS protected changes")

return shas_to_check


if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Rolling release update")
parser.add_argument("--repo", help="Repository path", required=True)
Expand Down Expand Up @@ -250,7 +166,12 @@ def check_for_fips_protected_changes(repo, branch, common_tag):
print(repo.git.show('--pretty="%H %s"', "-s", common_sha.decode()))

print("[rolling release update] Checking for FIPS protected changes between the common tag and HEAD")
shas_to_check = check_for_fips_protected_changes(repo, args.new_base_branch, common_sha)
repo.git.checkout(args.new_base_branch)
try:
shas_to_check = check_for_fips_protected_changes(args.repo, common_sha.decode(), "HEAD")
except RuntimeError as e:
print(f"[rolling release update] {e}")
exit(1)
if shas_to_check and args.fips_override is False:
for sha, dir in shas_to_check.items():
print(f"## Commit {sha.decode()}")
Expand Down