diff --git a/.github/workflows/pull-request-lint.yml b/.github/workflows/pull-request-lint.yml index 51bc55b4..670d389b 100644 --- a/.github/workflows/pull-request-lint.yml +++ b/.github/workflows/pull-request-lint.yml @@ -38,19 +38,20 @@ jobs: env: GH_TOKEN: ${{ github.token }} PR_LABELS_JSON: ${{ toJson(github.event.pull_request.labels.*.name) }} + PR_NUMBER_INPUT: ${{ github.event.pull_request.number }} steps: - name: Get PR info id: get-pr run: | - if [ "${{ github.event_name }}" == "merge_group" ]; then - PR_NUMBER=$(echo "${{ github.ref }}" | grep -oP '(?<=/pr-)\d+' || echo "") - PR_LABELS=$(gh api repos/${{ github.repository }}/pulls/$PR_NUMBER | jq -c '[.labels[].name] // []') + if [ "$GITHUB_EVENT_NAME" == "merge_group" ]; then + PR_NUMBER=$(echo "$GITHUB_REF" | grep -oP '(?<=/pr-)\d+' || echo "") + PR_LABELS=$(gh api "repos/$GITHUB_REPOSITORY/pulls/$PR_NUMBER" | jq -c '[.labels[].name] // []') echo "::group::Getting Information" - gh api repos/${{ github.repository }}/pulls/$PR_NUMBER + gh api "repos/$GITHUB_REPOSITORY/pulls/$PR_NUMBER" echo $PR_LABELS echo "::endgroup::" - elif [ "${{ github.event_name }}" == "pull_request" -o "${{ github.event_name }}" == "pull_request_target" ]; then - PR_NUMBER="${{ github.event.pull_request.number }}" + elif [ "$GITHUB_EVENT_NAME" == "pull_request" -o "$GITHUB_EVENT_NAME" == "pull_request_target" ]; then + PR_NUMBER="$PR_NUMBER_INPUT" PR_LABELS=$(echo "$PR_LABELS_JSON" | jq -c '.') fi echo "::group::Debug Output Values" diff --git a/.gitleaks.toml b/.gitleaks.toml index b9f4b516..8d4a82bb 100644 --- a/.gitleaks.toml +++ b/.gitleaks.toml @@ -12,4 +12,5 @@ description = "Global allowlist" paths = [ '''\.gitleaks-baseline\.json$''', '''uv\.lock$''', + '''packages/shared/tests/test_credential_scrubber\.py$''', ] diff --git a/scripts/aidlc-evaluator/packages/quantitative/src/quantitative/analyzers.py b/scripts/aidlc-evaluator/packages/quantitative/src/quantitative/analyzers.py index f214bdb8..ecd1b472 100644 --- a/scripts/aidlc-evaluator/packages/quantitative/src/quantitative/analyzers.py +++ b/scripts/aidlc-evaluator/packages/quantitative/src/quantitative/analyzers.py @@ -107,6 +107,7 @@ def run_ruff(project_root: Path) -> ToolResult: sev = "error" if item.get("code", "").startswith("E") else "warning" raw_path = item.get("filename", "?") try: + # nosemgrep: ai.ai-best-practices.hooks-path-traversal - relative_to() enforces path stays within project_root; ValueError on escape rel_path = str(Path(raw_path).relative_to(project_root)) except ValueError: rel_path = raw_path @@ -320,6 +321,7 @@ def run_semgrep(project_root: Path) -> ToolResult: sev = _SEMGREP_SEVERITY_MAP.get(raw_sev, "medium") raw_path = item.get("path", "?") try: + # nosemgrep: ai.ai-best-practices.hooks-path-traversal - relative_to() enforces path stays within project_root; ValueError on escape rel_path = str(Path(raw_path).relative_to(project_root)) except ValueError: rel_path = raw_path diff --git a/scripts/aidlc-evaluator/packages/shared/tests/test_credential_scrubber.py b/scripts/aidlc-evaluator/packages/shared/tests/test_credential_scrubber.py index 0ad48442..4ec8c6cd 100644 --- a/scripts/aidlc-evaluator/packages/shared/tests/test_credential_scrubber.py +++ b/scripts/aidlc-evaluator/packages/shared/tests/test_credential_scrubber.py @@ -26,14 +26,14 @@ def test_aws_secret_key(self): def test_jwt_token(self): """Test JWT token redaction.""" - text = "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c" + text = "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c" # nosemgrep: generic.secrets.security.detected-jwt-token # gitleaks:allow result = scrub_credentials(text) assert "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9" not in result assert "[REDACTED-JWT-TOKEN]" in result def test_github_token(self): """Test GitHub personal access token redaction.""" - text = "GITHUB_TOKEN=ghp_1234567890abcdefghijklmnopqrstuv" + text = "GITHUB_TOKEN=ghp_1234567890abcdefghijklmnopqrstuv" # gitleaks:allow result = scrub_credentials(text) assert "ghp_1234567890abcdefghijklmnopqrstuv" not in result assert "[REDACTED-GITHUB-TOKEN]" in result @@ -60,7 +60,7 @@ def test_private_key(self): def test_api_key_hex(self): """Test generic API key redaction (hex format).""" - text = "api_key=a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4" + text = "api_key=a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4" # nosemgrep: generic.secrets.security.detected-generic-api-key # gitleaks:allow result = scrub_credentials(text) assert "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4" not in result assert "[REDACTED-API-KEY]" in result @@ -70,7 +70,7 @@ def test_multiple_credentials(self): text = """ AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY - TOKEN=ghp_1234567890abcdefghijklmnopqrstuv + TOKEN=ghp_1234567890abcdefghijklmnopqrstuv # gitleaks:allow """ result = scrub_credentials(text) assert "AKIAIOSFODNN7EXAMPLE" not in result @@ -104,7 +104,7 @@ class TestScrubDictValues: def test_scrub_all_strings(self): """Test scrubbing all string values in a dict.""" data = { - "token": "ghp_1234567890abcdefghijklmnopqrstuv", + "token": "ghp_1234567890abcdefghijklmnopqrstuv", # gitleaks:allow "count": 42, "message": "Hello world", } @@ -116,8 +116,8 @@ def test_scrub_all_strings(self): def test_scrub_specific_keys(self): """Test scrubbing only targeted keys.""" data = { - "token": "ghp_1234567890abcdefghijklmnopqrstuv", - "message": "ghp_1234567890abcdefghijklmnopqrstuv", + "token": "ghp_1234567890abcdefghijklmnopqrstuv", # gitleaks:allow + "message": "ghp_1234567890abcdefghijklmnopqrstuv", # gitleaks:allow } result = scrub_dict_values(data, keys_to_scrub={"token"}) assert "ghp_1234567890abcdefghijklmnopqrstuv" not in result["token"] diff --git a/scripts/aidlc-evaluator/packages/trend-reports/src/trend_reports/fetcher.py b/scripts/aidlc-evaluator/packages/trend-reports/src/trend_reports/fetcher.py index 8f0ec356..2e74d103 100644 --- a/scripts/aidlc-evaluator/packages/trend-reports/src/trend_reports/fetcher.py +++ b/scripts/aidlc-evaluator/packages/trend-reports/src/trend_reports/fetcher.py @@ -138,6 +138,8 @@ def fetch_workflow_runs( if event is not None: cmd.extend(["--event", event]) + # nosec B603, B607 - cmd is a static gh CLI invocation with validated string arguments (repo, branch, event) + # nosemgrep: python.lang.security.audit.dangerous-subprocess-use-audit.dangerous-subprocess-use-audit result = subprocess.run(cmd, capture_output=True, text=True, check=False) if result.returncode != 0: raise FetchError(f"Failed to list workflow runs for {repo}: {result.stderr.strip()}") diff --git a/scripts/aidlc-evaluator/packages/trend-reports/tests/test_models.py b/scripts/aidlc-evaluator/packages/trend-reports/tests/test_models.py index c96b742e..c7d7134e 100644 --- a/scripts/aidlc-evaluator/packages/trend-reports/tests/test_models.py +++ b/scripts/aidlc-evaluator/packages/trend-reports/tests/test_models.py @@ -50,7 +50,7 @@ def test_ordering(self): assert SemVer(0, 1, 9) < SemVer(1, 0, 0) def test_equality(self): - assert SemVer(1, 2, 3) == SemVer(1, 2, 3) + assert SemVer(1, 2, 3) == SemVer(1, 2, 3) # nosemgrep: template.eqeq-is-bad - dataclass equality via __eq__ is intentional here def test_frozen(self): sv = SemVer(1, 2, 3)