From 299ead89b341239f172432792288f635ec07a592 Mon Sep 17 00:00:00 2001 From: Adam Moussa <166072409+amoussa1229@users.noreply.github.com> Date: Thu, 23 Jul 2026 15:10:45 -0400 Subject: [PATCH] chore: resolve open code scanning alerts (URL host matching + workflow permissions) (#36) * fix: Refactor `comment_intent` to use `_contains_url_with_host` for URL host matching. * ci: add least-privilege permissions blocks to workflow callers Resolves code scanning alerts #4 and #5 (actions/missing-workflow-permissions). Both callable workflows only need contents: read; the dependency-review callable already declares it internally, this caps the caller token to match. --- .github/workflows/ci.yaml | 3 +++ .github/workflows/dependency-review.yml | 4 ++++ lambdas/classifier/classify.py | 16 +++++++++++++++- 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index e4be9ff..2388fb5 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -3,6 +3,9 @@ on: pull_request: branches: [main] +permissions: + contents: read + jobs: ci: uses: Sea-Haven-Industries/.github/.github/workflows/ci-python-sam.yaml@fd60e4c9041784f666ac0fdefb9bec3c7fbf5143 # main diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml index 2cd8119..42002bb 100644 --- a/.github/workflows/dependency-review.yml +++ b/.github/workflows/dependency-review.yml @@ -1,6 +1,10 @@ name: Dependency Review on: pull_request: + +permissions: + contents: read + jobs: review: uses: Sea-Haven-Industries/.github/.github/workflows/callable-dependency-review.yaml@fd60e4c9041784f666ac0fdefb9bec3c7fbf5143 # main diff --git a/lambdas/classifier/classify.py b/lambdas/classifier/classify.py index c2493c3..1c575b4 100644 --- a/lambdas/classifier/classify.py +++ b/lambdas/classifier/classify.py @@ -25,6 +25,7 @@ from __future__ import annotations import html import os import re +from urllib.parse import unquote, urlparse # Axis 2 — Hold Reason → category. HOLD_TO_CATEGORY = { @@ -118,6 +119,19 @@ def strip_html(comment: str | None) -> str: # --------------------------------------------------------------------------- +def _contains_url_with_host(text: str, expected_host: str) -> bool: + """Return True when ''text'' contains an absolute URL with hostname ''expected_host''.""" + for raw_url in re.findall(r"https?://[^\s<>'\"()]+", text, flags=re.IGNORECASE): + candidate = unquote(raw_url).rstrip(".,;:!?)]}\"'") + try: + host = urlparse(candidate).hostname + except ValueError: + continue + if host and host.lower() == expected_host: + return True + return False + + def comment_intent(comment: str) -> str | None: """Ordered, most-specific-first rule ladder over the *stripped* text. @@ -147,7 +161,7 @@ def comment_intent(comment: str) -> str | None: # -- SIM Ticket: Amazon SIM tooling references. if ( re.search(r"\bsim\b.*\b(ticket|tt|v\d{6,})\b", low) - or "t.corp.amazon.com" in low + or _contains_url_with_host(t, "t.corp.amazon.com") or re.search(r"\bsim\s*tt\b", low) ): return "SIM Ticket"