Skip to content

Commit c5b478e

Browse files
AriESQclaude
andcommitted
Skip untracked AI context files in pre-flight scan
The scanner flagged files like CLAUDE.md or .cursor/ on disk regardless of git status, but `push_local` publishes only committed history — so untracked / gitignored AI context files are never pushed and reporting them is a false positive. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent fd87b68 commit c5b478e

1 file changed

Lines changed: 41 additions & 17 deletions

File tree

gh_safe_repo/security_scanner.py

Lines changed: 41 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,22 @@ def _is_committed(self, root_path: str, rel_dir: str) -> bool:
224224
except FileNotFoundError:
225225
return False
226226

227+
def _is_tracked(self, root_path: str, rel_path: str) -> bool:
228+
"""Return True if rel_path is tracked in git (in the index).
229+
230+
Untracked / gitignored files are not pushed by `git push`, so flagging
231+
them as AI-context leaks would be a false positive for the publish
232+
workflow. On any git error, return True (be conservative — flag).
233+
"""
234+
try:
235+
result = subprocess.run(
236+
["git", "-C", root_path, "ls-files", "--error-unmatch", "--", rel_path],
237+
capture_output=True, text=True,
238+
)
239+
return result.returncode == 0
240+
except FileNotFoundError:
241+
return True
242+
227243
# --- Discovery ---
228244

229245
def _detect_native(self) -> Optional[str]:
@@ -431,15 +447,20 @@ def _unified_walk(
431447
full_path = os.path.join(dirpath, d)
432448
rel_path = os.path.relpath(full_path, root_path).replace(os.sep, "/")
433449
dirs.remove(d)
434-
if not self._is_excluded(rel_path):
435-
findings.append(Finding(
436-
severity=Severity.CRITICAL,
437-
category=FindingCategory.AI_CONTEXT_FILE,
438-
file_path=rel_path,
439-
line_number=0,
440-
rule="AI context file",
441-
match=_ai_context_hint(rel_path),
442-
))
450+
if self._is_excluded(rel_path):
451+
continue
452+
# In a git repo, only flag if something under the dir
453+
# is tracked — untracked/ignored files won't be pushed.
454+
if is_git_repo and not self._is_committed(root_path, rel_path):
455+
continue
456+
findings.append(Finding(
457+
severity=Severity.CRITICAL,
458+
category=FindingCategory.AI_CONTEXT_FILE,
459+
file_path=rel_path,
460+
line_number=0,
461+
rule="AI context file",
462+
match=_ai_context_hint(rel_path),
463+
))
443464

444465
for filename in files:
445466
full_path = os.path.join(dirpath, filename)
@@ -453,14 +474,17 @@ def _unified_walk(
453474
if self._warn_ai_context_files:
454475
if (filename.lower() in _AI_CONTEXT_BASENAMES
455476
or rel_path.lower() in _AI_CONTEXT_REL_PATHS):
456-
findings.append(Finding(
457-
severity=Severity.CRITICAL,
458-
category=FindingCategory.AI_CONTEXT_FILE,
459-
file_path=rel_path,
460-
line_number=0,
461-
rule="AI context file",
462-
match=_ai_context_hint(rel_path),
463-
))
477+
# In a git repo, only flag tracked files —
478+
# untracked/gitignored files won't be pushed.
479+
if not is_git_repo or self._is_tracked(root_path, rel_path):
480+
findings.append(Finding(
481+
severity=Severity.CRITICAL,
482+
category=FindingCategory.AI_CONTEXT_FILE,
483+
file_path=rel_path,
484+
line_number=0,
485+
rule="AI context file",
486+
match=_ai_context_hint(rel_path),
487+
))
464488

465489
# Large file check
466490
try:

0 commit comments

Comments
 (0)