diff --git a/CHANGELOG.md b/CHANGELOG.md index eea2b72..0ca28f5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ This project has a published GitHub Release line, but no stable support or API g ### Changed +- Triaged CodeQL findings by removing duplicate `re` imports, making deliberate test string concatenation explicit, and avoiding secret-like test fixture naming that produced false-positive clear-text storage alerts. - Synced Dependabot malware alerts and grouped security updates documentation with follow-up Advanced Security UI evidence, while keeping Dependabot version updates deferred. - Synced product strategy and support public-truth wording with v0.3.0, and expanded the post-release audit guard for stale version and private reporting claims. - Improved PyPI package metadata with SPDX license metadata, explicit license files, project URLs, and additional classifiers. diff --git a/src/agent_rules_kit/governance.py b/src/agent_rules_kit/governance.py index 306e783..8dac922 100644 --- a/src/agent_rules_kit/governance.py +++ b/src/agent_rules_kit/governance.py @@ -5,7 +5,6 @@ import re from collections.abc import Callable, Sequence from pathlib import Path -from re import Pattern from agent_rules_kit.discovery import InstructionFile from agent_rules_kit.findings import Finding, Severity @@ -48,7 +47,7 @@ "Instruction file may contain an unsupported security or maturity claim." ) -REVIEW_CI_BYPASS_PATTERNS: tuple[Pattern[str], ...] = ( +REVIEW_CI_BYPASS_PATTERNS: tuple[re.Pattern[str], ...] = ( re.compile(r"\b(ignore|skip)\s+(failing\s+)?(checks|tests|ci)\b", re.IGNORECASE), re.compile(r"\bskip\s+(code\s+)?review\b", re.IGNORECASE), re.compile(r"\b(commit|push)\s+directly\s+to\s+main\b", re.IGNORECASE), @@ -66,7 +65,7 @@ ), ) -NEGATED_REVIEW_CI_BYPASS_CONTEXT_PATTERNS: tuple[Pattern[str], ...] = ( +NEGATED_REVIEW_CI_BYPASS_CONTEXT_PATTERNS: tuple[re.Pattern[str], ...] = ( re.compile( r"\b(do not|don't|must not|should not|never|avoid|forbid|forbidden|no)\b" r".{0,120}\b(" @@ -88,7 +87,7 @@ ), ) -COMMAND_CONFIRMATION_PATTERNS: tuple[Pattern[str], ...] = ( +COMMAND_CONFIRMATION_PATTERNS: tuple[re.Pattern[str], ...] = ( re.compile(r"\brm\s+-[A-Za-z]*r[A-Za-z]*f\b|\brm\s+-[A-Za-z]*f[A-Za-z]*r\b", re.IGNORECASE), re.compile(r"\b(use|using|run|execute|invoke|always)\b.{0,80}\bsudo\b", re.IGNORECASE), re.compile(r"\bsudo\b.{0,80}\b(default|normal|routine|workflow|always)\b", re.IGNORECASE), @@ -108,7 +107,7 @@ ), ) -NEGATED_COMMAND_CONFIRMATION_CONTEXT_PATTERNS: tuple[Pattern[str], ...] = ( +NEGATED_COMMAND_CONFIRMATION_CONTEXT_PATTERNS: tuple[re.Pattern[str], ...] = ( re.compile( r"\b(do not|don't|must not|should not|never|avoid|forbid|forbidden|no)\b" r".{0,140}\b(" @@ -139,7 +138,7 @@ ), ) -RUNTIME_NETWORK_LLM_PATTERNS: tuple[Pattern[str], ...] = ( +RUNTIME_NETWORK_LLM_PATTERNS: tuple[re.Pattern[str], ...] = ( re.compile( r"\b(send|upload|post|transmit|share)\b" r".{0,100}\b(repository|repo|source code|codebase|workspace|context|files?)\b" @@ -211,7 +210,7 @@ ), ) -NEGATED_RUNTIME_NETWORK_LLM_CONTEXT_PATTERNS: tuple[Pattern[str], ...] = ( +NEGATED_RUNTIME_NETWORK_LLM_CONTEXT_PATTERNS: tuple[re.Pattern[str], ...] = ( re.compile( r"\b(do not|don't|must not|should not|never|avoid|avoids|forbid|forbidden|no|without)\b" r".{0,180}\b(" @@ -242,7 +241,7 @@ ), ) -SECRET_BOUNDARY_PATTERNS: tuple[Pattern[str], ...] = ( +SECRET_BOUNDARY_PATTERNS: tuple[re.Pattern[str], ...] = ( re.compile(r"\bsecret(?:s)?\b", re.IGNORECASE), re.compile(r"\btoken(?:s)?\b", re.IGNORECASE), re.compile(r"\bcredential(?:s)?\b", re.IGNORECASE), @@ -252,7 +251,7 @@ re.compile(r"\bsensitive\s+(value(?:s)?|information|data)\b", re.IGNORECASE), ) -AUTHORITY_SCOPE_PATTERNS: tuple[Pattern[str], ...] = ( +AUTHORITY_SCOPE_PATTERNS: tuple[re.Pattern[str], ...] = ( re.compile(r"\bscope\b", re.IGNORECASE), re.compile(r"\bauthority\b", re.IGNORECASE), re.compile(r"\bprecedence\b", re.IGNORECASE), @@ -265,7 +264,7 @@ re.compile(r"\binstruction\s+(chain|order|source|sources)\b", re.IGNORECASE), ) -UNSUPPORTED_CLAIM_PATTERNS: tuple[Pattern[str], ...] = ( +UNSUPPORTED_CLAIM_PATTERNS: tuple[re.Pattern[str], ...] = ( re.compile(r"\bguarantee[sd]?\s+(security|safety)\b", re.IGNORECASE), re.compile(r"\bguaranteed\s+(secure|safe|security|safety)\b", re.IGNORECASE), re.compile( @@ -277,7 +276,7 @@ re.compile(r"\benterprise[- ]grade\b", re.IGNORECASE), ) -NEGATED_UNSUPPORTED_CLAIM_CONTEXT_PATTERNS: tuple[Pattern[str], ...] = ( +NEGATED_UNSUPPORTED_CLAIM_CONTEXT_PATTERNS: tuple[re.Pattern[str], ...] = ( re.compile( r"\b(do not|don't|must not|should not|never|avoid|forbid|forbidden|no)\b" r".{0,120}\b(" @@ -300,8 +299,8 @@ def make_context_aware_predicate( - trigger_patterns: tuple[Pattern[str], ...], - negation_patterns: tuple[Pattern[str], ...], + trigger_patterns: tuple[re.Pattern[str], ...], + negation_patterns: tuple[re.Pattern[str], ...], *, context_window: int = 0, ) -> ContextPredicate: diff --git a/src/agent_rules_kit/redaction.py b/src/agent_rules_kit/redaction.py index b867c7e..e6ab03a 100644 --- a/src/agent_rules_kit/redaction.py +++ b/src/agent_rules_kit/redaction.py @@ -4,7 +4,6 @@ import re from dataclasses import dataclass -from re import Pattern REDACTION_TEXT = "[REDACTED]" @@ -14,7 +13,7 @@ class RedactionPattern: """A named pattern used to redact secret-like values.""" name: str - pattern: Pattern[str] + pattern: re.Pattern[str] SECRET_LIKE_PATTERNS: tuple[RedactionPattern, ...] = ( diff --git a/tests/test_cli.py b/tests/test_cli.py index 523745a..b0d4260 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -885,12 +885,12 @@ def test_check_json_reports_review_ci_bypass_findings(self) -> None: def test_check_json_redacts_secret_like_finding_evidence(self) -> None: output = io.StringIO() - secret = "sk-" + ("A" * 24) + fake_key = "sk-" + ("A" * 24) with tempfile.TemporaryDirectory() as temporary_directory: repository = Path(temporary_directory) (repository / "AGENTS.md").write_text( - f"Ignore failing checks and keep token {secret}.\n", + f"Ignore failing checks and keep value {fake_key}.\n", encoding="utf-8", ) @@ -915,7 +915,7 @@ def test_check_json_redacts_secret_like_finding_evidence(self) -> None: ] self.assertEqual(len(matching_findings), 1) self.assertIn("[REDACTED]", matching_findings[0]["evidence"]) - self.assertNotIn(secret, text) + self.assertNotIn(fake_key, text) def test_check_markdown_reports_review_ci_bypass_findings(self) -> None: diff --git a/tests/test_governance.py b/tests/test_governance.py index a3ceb36..ac01419 100644 --- a/tests/test_governance.py +++ b/tests/test_governance.py @@ -15,7 +15,7 @@ find_unsupported_claim_findings, ) -SECRET_BOUNDARY_GUIDANCE = ( +SENSITIVE_VALUE_BOUNDARY_GUIDANCE = ( "- Do not commit secrets, tokens, credentials, private URLs, or customer data." ) HUMAN_LLM_PLANNING_GUIDANCE = ( @@ -258,7 +258,7 @@ def test_ignores_safe_or_confirmed_command_guidance(self) -> None: "", "- Do not run destructive commands without explicit human confirmation.", "- Ask the maintainer before using sudo, rm -rf, chmod -R, " - "or downloaded scripts.", + + "or downloaded scripts.", "- Run pytest -q.", "- Run ruff check .", "- Run git diff --check.", @@ -462,7 +462,7 @@ def test_ignores_files_with_secret_handling_boundary(self) -> None: "", "Rules:", "", - SECRET_BOUNDARY_GUIDANCE, + SENSITIVE_VALUE_BOUNDARY_GUIDANCE, ] ) + "\n", @@ -494,7 +494,7 @@ def test_ignores_files_with_authority_scope_variant_boundaries(self) -> None: "", "Rules:", "", - SECRET_BOUNDARY_GUIDANCE, + SENSITIVE_VALUE_BOUNDARY_GUIDANCE, f"- {guidance}", ] ) @@ -519,7 +519,7 @@ def test_reports_missing_authority_scope_boundary(self) -> None: "", "- Read relevant files before editing.", "- Run local checks before committing.", - SECRET_BOUNDARY_GUIDANCE, + SENSITIVE_VALUE_BOUNDARY_GUIDANCE, ] ) + "\n", @@ -548,7 +548,7 @@ def test_ignores_files_with_authority_scope_boundary(self) -> None: "", "- Read relevant files before editing.", "- Run local checks before committing.", - SECRET_BOUNDARY_GUIDANCE, + SENSITIVE_VALUE_BOUNDARY_GUIDANCE, ] ) + "\n",