diff --git a/.jules/sentinel.md b/.jules/sentinel.md index 79f94e70f3..90cab60a64 100644 --- a/.jules/sentinel.md +++ b/.jules/sentinel.md @@ -43,3 +43,7 @@ **Vulnerability:** Denial of Service / Availability **Learning:** Strix security scanners crashed when the backend LLM returned an 'internal server error' HTTP 500 response. This was because 'internal server error' string match was missing from the `is_llm_api_connection_error` function in the Strix retry gate. **Prevention:** Always include `internal server error` in string match conditions when handling HTTP API Connection exceptions for LLM backends to ensure proper fail-closed and retry handling. +## 2026-09-08 - Overly Permissive Regex for URL Parameters +**Vulnerability:** Permissive regex `^[A-Za-z0-9_.-]+$` used for repo and org names allowed leading/trailing/consecutive dots, creating SSRF and path traversal risks when constructing URLs. +**Learning:** Basic alphanumeric regex with dots is insufficient for URL parameter validation as it permits path traversal sequences like `..`. +**Prevention:** Use negative lookaheads `^(?!.*(?:\.\.|\.$))[A-Za-z0-9_.-]+$` to explicitly reject trailing and consecutive dots in URL parameters. diff --git a/scripts/ci/agent_mention_sweep.py b/scripts/ci/agent_mention_sweep.py index 50e0a84f17..5b56fdcf4f 100755 --- a/scripts/ci/agent_mention_sweep.py +++ b/scripts/ci/agent_mention_sweep.py @@ -22,8 +22,8 @@ ) from redact_sensitive_log import redact_text -ORG_NAME_RE = re.compile(r"^[A-Za-z0-9_.-]+$") -REPOSITORY_RE = re.compile(r"^ContextualWisdomLab/[A-Za-z0-9_.-]+$") +ORG_NAME_RE = re.compile(r"^(?!.*(?:\.\.|\.$))[A-Za-z0-9_.-]+$") +REPOSITORY_RE = re.compile(r"^ContextualWisdomLab/(?!.*(?:\.\.|\.$))[A-Za-z0-9_.-]+$") REPOSITORY_SOURCES = frozenset({"organization", "installation"}) REPOSITORY_ROTATION_SECONDS = 5 * 60 # The sweep-organization-agent-mentions job has a 900s (15-minute) GitHub diff --git a/scripts/ci/organization_commercial_readiness_loop.py b/scripts/ci/organization_commercial_readiness_loop.py index 9657bd2d4d..a8d5419c7e 100644 --- a/scripts/ci/organization_commercial_readiness_loop.py +++ b/scripts/ci/organization_commercial_readiness_loop.py @@ -26,7 +26,7 @@ DEFAULT_ORGANIZATION = "ContextualWisdomLab" -ORGANIZATION_RE = re.compile(r"^[A-Za-z0-9_.-]+$") +ORGANIZATION_RE = re.compile(r"^(?!.*(?:\.\.|\.$))[A-Za-z0-9_.-]+$") ENTRYPOINT_MARKER = "# cwl-org-commercial-entrypoint: v1" CENTRAL_REPOSITORY = f"{DEFAULT_ORGANIZATION}/.github" CENTRAL_REPAIR_EVENT = "pr-review-fix-scheduler" diff --git a/scripts/ci/reconcile_repository_labels.py b/scripts/ci/reconcile_repository_labels.py index d4585877c6..761cfb6749 100644 --- a/scripts/ci/reconcile_repository_labels.py +++ b/scripts/ci/reconcile_repository_labels.py @@ -14,7 +14,7 @@ ORGANIZATION = "ContextualWisdomLab" -REPOSITORY_RE = re.compile(r"^[A-Za-z0-9_.-]+$") +REPOSITORY_RE = re.compile(r"^(?!.*(?:\.\.|\.$))[A-Za-z0-9_.-]+$") class TaxonomyError(ValueError): diff --git a/scripts/ci/reconcile_repository_metadata.py b/scripts/ci/reconcile_repository_metadata.py index 36a910ffa8..1570455818 100644 --- a/scripts/ci/reconcile_repository_metadata.py +++ b/scripts/ci/reconcile_repository_metadata.py @@ -21,7 +21,7 @@ ORGANIZATION = "ContextualWisdomLab" -REPOSITORY_RE = re.compile(r"^[A-Za-z0-9_.-]+$") +REPOSITORY_RE = re.compile(r"^(?!.*(?:\.\.|\.$))[A-Za-z0-9_.-]+$") TOPIC_RE = re.compile(r"^[a-z0-9][a-z0-9-]{0,49}$") MAX_DESCRIPTION_CHARS = 350 PAGES_BASE_URL = f"https://{ORGANIZATION.casefold()}.github.io"