Skip to content
Closed
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
4 changes: 4 additions & 0 deletions .jules/bolt.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,7 @@
## 2026-07-09 - Avoid N+1 API blocking in SBOM aggregator
**Learning:** The `collect_inventories` function in `scripts/ci/sbom_inventory_aggregator.py` was fetching SBOMs from the GitHub dependency graph synchronously for every repository in the organization. For large organizations (up to 500 repos), this N+1 network/CLI bottleneck significantly stalled the aggregation workflow.
**Action:** Use `concurrent.futures.ThreadPoolExecutor` to fetch SBOMs concurrently when multiple repositories are provided, bounded by a `max_workers` limit (e.g., 10) to avoid overwhelming the CLI/API, while preserving the fast serial path for single-item inputs.

## 2026-07-23 - [O(n²) to O(n) optimization in linear string scanning]
**Learning:** In `redact_sensitive_log.py`, scanning character by character and failing back by only 1 character caused O(n²) behavior on long strings of text.
**Action:** Return the parsed cursor position along with a failure `None` to jump ahead and prevent redundant parsing, improving parsing speed dramatically on non-matching large strings.
47 changes: 29 additions & 18 deletions scripts/ci/redact_sensitive_log.py
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
from typing import Any

REDACTED = "[REDACTED]"
KEY_CHARS = frozenset("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_.-")
KEY_CHARS = frozenset(
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_.-"
)
SENSITIVE_KEY_RE = re.compile(
r"(?:token|secret|password|passwd|credential|authorization|jwt|"
r"api[_-]?key|private[_-]?key|access[_-]?key|session[_-]?key)",
Expand All @@ -20,8 +22,7 @@
r"[A-Za-z0-9_-]{3,}(?![A-Za-z0-9_-])"
)
BEARER_RE = re.compile(
r"(?P<prefix>\b(?:authorization\s*:\s*)?(?:bearer|basic)\s+)"
r"[^\s\"'\\]+",
r"(?P<prefix>\b(?:authorization\s*:\s*)?(?:bearer|basic)\s+)" r"[^\s\"'\\]+",
re.IGNORECASE,
)
PROVIDER_TOKEN_RES = (
Expand All @@ -44,7 +45,7 @@ def _redact_json(value: Any) -> Any:
return value


def _consume_sensitive_assignment(text: str, start: int) -> tuple[str, int] | None:
def _consume_sensitive_assignment(text: str, start: int) -> tuple[str | None, int]:
"""Return a redacted key/value assignment parsed in linear time."""
Comment on lines +48 to 49
cursor = start
key_quote = ""
Expand All @@ -53,25 +54,31 @@ def _consume_sensitive_assignment(text: str, start: int) -> tuple[str, int] | No
cursor += 1
key_start = cursor
if cursor >= len(text) or text[cursor] not in KEY_CHARS or text[cursor].isdigit():
return None
return None, start + 1
while cursor < len(text) and text[cursor] in KEY_CHARS:
cursor += 1
key = text[key_start:cursor]

parsed_end = cursor
fail_ret = start + 1 if key_quote else parsed_end

if key_quote:
if cursor >= len(text) or text[cursor] != key_quote:
return None
return None, fail_ret
cursor += 1

if not SENSITIVE_KEY_RE.search(key):
return None
return None, fail_ret

while cursor < len(text) and text[cursor].isspace():
cursor += 1
if cursor >= len(text) or text[cursor] not in ":=":
return None
return None, fail_ret
cursor += 1
while cursor < len(text) and text[cursor].isspace():
cursor += 1
if cursor >= len(text):
return None
return None, fail_ret

value_start = cursor
if text[cursor] in "\"'":
Expand All @@ -88,10 +95,14 @@ def _consume_sensitive_assignment(text: str, start: int) -> tuple[str, int] | No
elif char == value_quote:
break
else:
while cursor < len(text) and not text[cursor].isspace() and text[cursor] not in ",}":
while (
cursor < len(text)
and not text[cursor].isspace()
and text[cursor] not in ",}"
):
cursor += 1
if cursor == value_start:
return None
return None, fail_ret
return text[start:value_start] + REDACTED, cursor


Expand All @@ -100,13 +111,13 @@ def _redact_assignments(text: str) -> str:
output: list[str] = []
cursor = 0
while cursor < len(text):
match = _consume_sensitive_assignment(text, cursor)
if match is None:
output.append(text[cursor])
cursor += 1
continue
replacement, cursor = match
output.append(replacement)
replacement, next_cursor = _consume_sensitive_assignment(text, cursor)
if replacement is None:
output.append(text[cursor:next_cursor])
cursor = next_cursor
else:
output.append(replacement)
cursor = next_cursor
Comment on lines +114 to +120
return "".join(output)


Expand Down
Loading