Skip to content

Commit fecf525

Browse files
committed
Add standalone FIPS protected directory check tool
Pulled the FIPS protected directory list and change detection logic out of rolling-release-update.py into kt/ktlib/ciq_helpers.py so it can be reused. Added check_fips_changes.py as a standalone CLI that wraps the shared library function for use by CI workflows and scripts. rolling-release-update.py now imports from ciq_helpers instead of carrying its own copy of the check.
1 parent d8589c6 commit fecf525

3 files changed

Lines changed: 173 additions & 89 deletions

File tree

check_fips_changes.py

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
#!/usr/bin/env python3
2+
"""Check for FIPS protected directory changes in a range of git commits.
3+
4+
Exits 0 if no FIPS changes found (or --fips-override is set).
5+
Exits 1 if FIPS changes are found without override.
6+
"""
7+
8+
import argparse
9+
import subprocess
10+
import sys
11+
12+
from kt.ktlib.ciq_helpers import FIPS_PROTECTED_DIRECTORIES, check_for_fips_protected_changes
13+
14+
15+
def main():
16+
parser = argparse.ArgumentParser(description="Check for FIPS protected directory changes")
17+
parser.add_argument("--repo", help="Path to git repository", default=".")
18+
parser.add_argument("--base-ref", help="Base ref (exclusive start of range)", required=True)
19+
parser.add_argument("--target-ref", help="Target ref (inclusive end of range)", required=True)
20+
parser.add_argument("--fips-override", help="Override FIPS check abort", action="store_true")
21+
args = parser.parse_args()
22+
23+
print(f"[fips-check] Checking for FIPS protected changes in {args.base_ref}..{args.target_ref}")
24+
print(f"[fips-check] Protected directories: {', '.join(d.decode() for d in FIPS_PROTECTED_DIRECTORIES)}")
25+
26+
try:
27+
fips_commits = check_for_fips_protected_changes(args.repo, args.base_ref, args.target_ref)
28+
except RuntimeError as e:
29+
print(f"[fips-check] ERROR: {e}", file=sys.stderr)
30+
sys.exit(1)
31+
32+
if not fips_commits:
33+
print("[fips-check] No FIPS protected changes found")
34+
sys.exit(0)
35+
36+
print("\n[fips-check] ========================================")
37+
print("[fips-check] FIPS protected changes detected")
38+
print("[fips-check] ========================================")
39+
print(f"[fips-check] {len(fips_commits)} commit(s) touch FIPS protected directories:\n")
40+
41+
for sha, dirs in fips_commits.items():
42+
result = subprocess.run(
43+
["git", "show", "--stat", sha.decode()],
44+
stdout=subprocess.PIPE,
45+
stderr=subprocess.PIPE,
46+
cwd=args.repo,
47+
)
48+
print(f"## Commit {sha.decode()}")
49+
if result.returncode == 0:
50+
print(result.stdout.decode("utf-8", "backslashreplace"))
51+
else:
52+
print(" (could not show commit details)")
53+
for d in sorted(dirs):
54+
print(f" FIPS directory: {d.decode()}")
55+
print()
56+
57+
if args.fips_override:
58+
print("[fips-check] --fips-override set, continuing despite FIPS protected changes")
59+
sys.exit(0)
60+
61+
print("[fips-check] Please contact the CIQ FIPS / Security team for further instructions")
62+
print("[fips-check] Use --fips-override to bypass this check")
63+
sys.exit(1)
64+
65+
66+
if __name__ == "__main__":
67+
main()

kt/ktlib/ciq_helpers.py

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -480,6 +480,102 @@ def read_spec_el_version(spec_lines):
480480
return _read_spec_define(spec_lines, "el_version", r"\d+")
481481

482482

483+
FIPS_PROTECTED_DIRECTORIES = [
484+
b"arch/x86/crypto/",
485+
b"crypto/asymmetric_keys/",
486+
b"crypto/",
487+
b"drivers/crypto/",
488+
b"drivers/char/random.c",
489+
b"include/crypto",
490+
]
491+
492+
493+
def check_for_fips_protected_changes(repo_path, start_ref, end_ref):
494+
"""Check for changes to FIPS protected directories in a range of commits.
495+
496+
Iterates over commits in start_ref..end_ref and checks whether any
497+
modified files fall under a FIPS protected directory. Uses bytestrings
498+
throughout to avoid encoding issues with international contributor names
499+
in git output.
500+
501+
Parameters:
502+
repo_path: Path to the git repository.
503+
start_ref: The starting ref (exclusive) for the commit range.
504+
end_ref: The ending ref (inclusive) for the commit range.
505+
506+
Returns:
507+
dict mapping commit SHA (bytes) -> set of matched FIPS directory prefixes (bytes)
508+
for each commit that touches FIPS protected paths. Empty dict if none found.
509+
"""
510+
print("[fips-check] Checking for FIPS protected changes")
511+
print(f"[fips-check] Getting SHAS {start_ref}..{end_ref}")
512+
results = subprocess.run(
513+
["git", "log", "--pretty=%H", f"{start_ref}..{end_ref}"],
514+
stderr=subprocess.PIPE,
515+
stdout=subprocess.PIPE,
516+
cwd=repo_path,
517+
)
518+
if results.returncode != 0:
519+
print(results.stderr)
520+
raise RuntimeError(f"git log failed for range {start_ref}..{end_ref}")
521+
522+
num_commits = len(results.stdout.split(b"\n"))
523+
print("[fips-check] Number of commits to check: ", num_commits)
524+
shas_to_check = {}
525+
commits_checked = 0
526+
527+
progress_interval = max(1, num_commits // 10)
528+
529+
print("[fips-check] Checking modifications of shas")
530+
for sha in results.stdout.split(b"\n"):
531+
commits_checked += 1
532+
if commits_checked % progress_interval == 0:
533+
print(f"[fips-check] Checked {commits_checked} of {num_commits} commits")
534+
if sha == b"":
535+
continue
536+
res = subprocess.run(
537+
["git", "show", "--name-only", "--pretty=%H %s", f"{sha.decode()}"],
538+
stderr=subprocess.PIPE,
539+
stdout=subprocess.PIPE,
540+
cwd=repo_path,
541+
)
542+
if res.returncode != 0:
543+
print(res)
544+
print(res.stderr)
545+
raise RuntimeError(f"git show failed for {sha}")
546+
547+
sha_hash_and_subject = b""
548+
touched_fips_files = set()
549+
550+
for line in res.stdout.split(b"\n"):
551+
if sha_hash_and_subject == b"":
552+
sha_hash_and_subject = line
553+
continue
554+
if line == b"":
555+
continue
556+
557+
add_to_check = False
558+
559+
for dir in FIPS_PROTECTED_DIRECTORIES:
560+
if line.startswith(dir):
561+
add_to_check = True
562+
if dir not in touched_fips_files:
563+
touched_fips_files.add(dir)
564+
565+
if add_to_check:
566+
shas_to_check[sha_hash_and_subject.split(b" ")[0]] = touched_fips_files
567+
568+
if touched_fips_files:
569+
print(f"[fips-check] Checked commit {sha} touched {len(touched_fips_files)} FIPS protected files")
570+
for f in touched_fips_files:
571+
print(f" - {f}")
572+
sha_hash_and_subject = b""
573+
574+
print(f"[fips-check] {len(shas_to_check)} of {num_commits} commits have FIPS protected changes")
575+
576+
return shas_to_check
577+
578+
483579
def run_cve_search(vulns_repo, kernel_repo, query) -> tuple[bool, Optional[str]]:
484580
"""
485581
Run the cve_search script from the vulns repo.

rolling-release-update.py

Lines changed: 10 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,10 @@
66

77
import git
88

9-
from kt.ktlib.ciq_helpers import get_backport_commit_data
10-
11-
FIPS_PROTECTED_DIRECTORIES = [
12-
b"arch/x86/crypto/",
13-
b"crypto/asymmetric_keys/",
14-
b"crypto/",
15-
b"drivers/crypto/",
16-
b"drivers/char/random.c",
17-
b"include/crypto",
18-
]
9+
from kt.ktlib.ciq_helpers import (
10+
check_for_fips_protected_changes,
11+
get_backport_commit_data,
12+
)
1913

2014
DEBUG = False
2115

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

110104

111-
def check_for_fips_protected_changes(repo, branch, common_tag):
112-
print("[rolling release update] Checking for FIPS protected changes")
113-
repo.git.checkout(branch)
114-
print(f"[rolling release update] Getting SHAS {common_tag.decode()}..HEAD")
115-
results = subprocess.run(
116-
["git", "log", "--pretty=%H", f"{common_tag.decode()}..HEAD"],
117-
stderr=subprocess.PIPE,
118-
stdout=subprocess.PIPE,
119-
cwd=repo.working_dir,
120-
)
121-
if results.returncode != 0:
122-
print(results.stderr)
123-
exit(1)
124-
125-
num_commits = len(results.stdout.split(b"\n"))
126-
print("[rolling release update] Number of commits to check: ", num_commits)
127-
shas_to_check = {}
128-
commits_checked = 0
129-
130-
progress_interval = max(1, num_commits // 10)
131-
132-
print("[rolling release update] Checking modifications of shas")
133-
if DEBUG:
134-
print(results.stdout.split(b"\n"))
135-
for sha in results.stdout.split(b"\n"):
136-
commits_checked += 1
137-
if commits_checked % progress_interval == 0:
138-
print(f"[rolling release update] Checked {commits_checked} of {num_commits} commits")
139-
if sha == b"":
140-
continue
141-
res = subprocess.run(
142-
["git", "show", "--name-only", "--pretty=%H %s", f"{sha.decode()}"],
143-
stderr=subprocess.PIPE,
144-
stdout=subprocess.PIPE,
145-
cwd=repo.working_dir,
146-
)
147-
if res.returncode != 0:
148-
print(res)
149-
print(res.stderr)
150-
exit(1)
151-
152-
sha_hash_and_subject = b""
153-
touched_fips_files = set()
154-
155-
for line in res.stdout.split(b"\n"):
156-
if sha_hash_and_subject == b"":
157-
sha_hash_and_subject = line
158-
continue
159-
if line == b"":
160-
continue
161-
162-
add_to_check = False
163-
164-
for dir in FIPS_PROTECTED_DIRECTORIES:
165-
if line.startswith(dir):
166-
if DEBUG:
167-
print(f"FIPS protected directory {dir} change found in commit {sha}")
168-
print(sha_hash_and_subject)
169-
add_to_check = True
170-
if dir not in touched_fips_files:
171-
touched_fips_files.add(dir)
172-
173-
if add_to_check:
174-
shas_to_check[sha_hash_and_subject.split(b" ")[0]] = touched_fips_files
175-
176-
if touched_fips_files:
177-
print(
178-
f"[rolling release update] Checked commit {sha} touched {len(touched_fips_files)} FIPS protected files"
179-
)
180-
for f in touched_fips_files:
181-
print(f" - {f}")
182-
sha_hash_and_subject = b""
183-
184-
print(f"[rolling release update] {len(shas_to_check)} of {num_commits} commits have FIPS protected changes")
185-
186-
return shas_to_check
187-
188-
189105
if __name__ == "__main__":
190106
parser = argparse.ArgumentParser(description="Rolling release update")
191107
parser.add_argument("--repo", help="Repository path", required=True)
@@ -250,7 +166,12 @@ def check_for_fips_protected_changes(repo, branch, common_tag):
250166
print(repo.git.show('--pretty="%H %s"', "-s", common_sha.decode()))
251167

252168
print("[rolling release update] Checking for FIPS protected changes between the common tag and HEAD")
253-
shas_to_check = check_for_fips_protected_changes(repo, args.new_base_branch, common_sha)
169+
repo.git.checkout(args.new_base_branch)
170+
try:
171+
shas_to_check = check_for_fips_protected_changes(args.repo, common_sha.decode(), "HEAD")
172+
except RuntimeError as e:
173+
print(f"[rolling release update] {e}")
174+
exit(1)
254175
if shas_to_check and args.fips_override is False:
255176
for sha, dir in shas_to_check.items():
256177
print(f"## Commit {sha.decode()}")

0 commit comments

Comments
 (0)