@@ -667,7 +667,30 @@ def _build_rule(rule_id: str, meta: Dict[str, str]) -> Dict[str, Any]:
667667
668668 @staticmethod
669669 def _build_result (finding : Finding , meta : Dict [str , str ]) -> Dict [str , Any ]:
670- uri = finding .file_path .replace ("\\ " , "/" )
670+ # Normalise to a relative URI so GitHub Code Scanning accepts the SARIF.
671+ # Absolute paths (CI runners, Windows) are stripped to be relative to
672+ # the repo root by removing any prefix up to and including the last
673+ # occurrence of common repo-root markers.
674+ import re as _re
675+
676+ raw_path = finding .file_path .replace ("\\ " , "/" )
677+ # Strip leading drive letters and absolute-path markers
678+ # e.g. /home/runner/work/PyAegis/PyAegis/pyaegis/foo.py -> pyaegis/foo.py
679+ # D:/Github/PyAegis/pyaegis/foo.py -> pyaegis/foo.py
680+ # Strategy: find the last segment that looks like a repo root by checking
681+ # for a known top-level directory name in the path.
682+ _TOP_DIRS = ("pyaegis/" , "tests/" , ".github/" )
683+ uri = raw_path
684+ for marker in _TOP_DIRS :
685+ idx = raw_path .rfind ("/" + marker )
686+ if idx != - 1 :
687+ uri = raw_path [idx + 1 :]
688+ break
689+ else :
690+ # fallback: strip everything before the first non-absolute segment
691+ uri = _re .sub (r"^(/[^/]+)+/" , "" , raw_path )
692+ if uri .startswith ("/" ):
693+ uri = uri .lstrip ("/" )
671694 level = _severity_to_sarif_level (finding .severity )
672695
673696 fix_obj = {
0 commit comments