rafter-cli 0.11.1__tar.gz → 0.11.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/PKG-INFO +2 -2
  2. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/pyproject.toml +4 -2
  3. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/rafter_cli/commands/agent.py +47 -0
  4. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/rafter_cli/commands/agent_components.py +15 -4
  5. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/rafter_cli/commands/hook.py +9 -0
  6. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/rafter_cli/resources/rafter-security-skill.md +1 -1
  7. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/rafter_cli/scanners/secret_patterns.py +7 -2
  8. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/README.md +0 -0
  9. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/rafter_cli/__init__.py +0 -0
  10. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/rafter_cli/__main__.py +0 -0
  11. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/rafter_cli/commands/__init__.py +0 -0
  12. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/rafter_cli/commands/backend.py +0 -0
  13. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/rafter_cli/commands/brief.py +0 -0
  14. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/rafter_cli/commands/ci.py +0 -0
  15. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/rafter_cli/commands/docs.py +0 -0
  16. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/rafter_cli/commands/issues/__init__.py +0 -0
  17. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/rafter_cli/commands/issues/dedup.py +0 -0
  18. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/rafter_cli/commands/issues/github_client.py +0 -0
  19. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/rafter_cli/commands/issues/issue_builder.py +0 -0
  20. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/rafter_cli/commands/issues/issues_app.py +0 -0
  21. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/rafter_cli/commands/mcp_server.py +0 -0
  22. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/rafter_cli/commands/notify.py +0 -0
  23. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/rafter_cli/commands/policy.py +0 -0
  24. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/rafter_cli/commands/report.py +0 -0
  25. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/rafter_cli/commands/scan.py +0 -0
  26. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/rafter_cli/commands/sites.py +0 -0
  27. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/rafter_cli/commands/skill.py +0 -0
  28. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/rafter_cli/commands/skill_remote.py +0 -0
  29. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/rafter_cli/core/__init__.py +0 -0
  30. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/rafter_cli/core/audit_logger.py +0 -0
  31. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/rafter_cli/core/command_interceptor.py +0 -0
  32. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/rafter_cli/core/config_manager.py +0 -0
  33. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/rafter_cli/core/config_schema.py +0 -0
  34. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/rafter_cli/core/custom_patterns.py +0 -0
  35. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/rafter_cli/core/docs_loader.py +0 -0
  36. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/rafter_cli/core/hook_control.py +0 -0
  37. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/rafter_cli/core/pattern_engine.py +0 -0
  38. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/rafter_cli/core/policy_loader.py +0 -0
  39. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/rafter_cli/core/risk_rules.py +0 -0
  40. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/rafter_cli/core/suppression_writer.py +0 -0
  41. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/rafter_cli/resources/__init__.py +0 -0
  42. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/rafter_cli/resources/agents/__init__.py +0 -0
  43. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/rafter_cli/resources/agents/rafter.md +0 -0
  44. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/rafter_cli/resources/continue-rules/rafter-code-review.md +0 -0
  45. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/rafter_cli/resources/continue-rules/rafter-secure-design.md +0 -0
  46. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/rafter_cli/resources/continue-rules/rafter-skill-review.md +0 -0
  47. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/rafter_cli/resources/continue-rules/rafter.md +0 -0
  48. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/rafter_cli/resources/cursor-rules/rafter-code-review.mdc +0 -0
  49. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/rafter_cli/resources/cursor-rules/rafter-secure-design.mdc +0 -0
  50. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/rafter_cli/resources/cursor-rules/rafter-skill-review.mdc +0 -0
  51. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/rafter_cli/resources/cursor-rules/rafter.mdc +0 -0
  52. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/rafter_cli/resources/pre-commit-hook.sh +0 -0
  53. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/rafter_cli/resources/pre-push-hook.sh +0 -0
  54. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/rafter_cli/resources/skills/__init__.py +0 -0
  55. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/rafter_cli/resources/skills/rafter/SKILL.md +0 -0
  56. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/rafter_cli/resources/skills/rafter/__init__.py +0 -0
  57. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/rafter_cli/resources/skills/rafter/docs/backend.md +0 -0
  58. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/rafter_cli/resources/skills/rafter/docs/cli-reference.md +0 -0
  59. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/rafter_cli/resources/skills/rafter/docs/finding-triage.md +0 -0
  60. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/rafter_cli/resources/skills/rafter/docs/guardrails.md +0 -0
  61. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/rafter_cli/resources/skills/rafter/docs/shift-left.md +0 -0
  62. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/rafter_cli/resources/skills/rafter-code-review/SKILL.md +0 -0
  63. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/rafter_cli/resources/skills/rafter-code-review/docs/api.md +0 -0
  64. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/rafter_cli/resources/skills/rafter-code-review/docs/asvs.md +0 -0
  65. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/rafter_cli/resources/skills/rafter-code-review/docs/cwe-top25.md +0 -0
  66. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/rafter_cli/resources/skills/rafter-code-review/docs/investigation-playbook.md +0 -0
  67. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/rafter_cli/resources/skills/rafter-code-review/docs/llm.md +0 -0
  68. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/rafter_cli/resources/skills/rafter-code-review/docs/web-app.md +0 -0
  69. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/rafter_cli/resources/skills/rafter-secure-design/SKILL.md +0 -0
  70. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/rafter_cli/resources/skills/rafter-secure-design/docs/api-design.md +0 -0
  71. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/rafter_cli/resources/skills/rafter-secure-design/docs/auth.md +0 -0
  72. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/rafter_cli/resources/skills/rafter-secure-design/docs/data-storage.md +0 -0
  73. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/rafter_cli/resources/skills/rafter-secure-design/docs/dependencies.md +0 -0
  74. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/rafter_cli/resources/skills/rafter-secure-design/docs/deployment.md +0 -0
  75. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/rafter_cli/resources/skills/rafter-secure-design/docs/ingestion.md +0 -0
  76. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/rafter_cli/resources/skills/rafter-secure-design/docs/standards-pointers.md +0 -0
  77. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/rafter_cli/resources/skills/rafter-secure-design/docs/threat-modeling.md +0 -0
  78. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/rafter_cli/resources/skills/rafter-skill-review/SKILL.md +0 -0
  79. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/rafter_cli/resources/skills/rafter-skill-review/docs/authorship-provenance.md +0 -0
  80. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/rafter_cli/resources/skills/rafter-skill-review/docs/changelog-review.md +0 -0
  81. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/rafter_cli/resources/skills/rafter-skill-review/docs/data-practices.md +0 -0
  82. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/rafter_cli/resources/skills/rafter-skill-review/docs/malware-indicators.md +0 -0
  83. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/rafter_cli/resources/skills/rafter-skill-review/docs/prompt-injection.md +0 -0
  84. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/rafter_cli/resources/skills/rafter-skill-review/docs/telemetry.md +0 -0
  85. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/rafter_cli/resources/windsurf-rules/rafter-code-review.md +0 -0
  86. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/rafter_cli/resources/windsurf-rules/rafter-secure-design.md +0 -0
  87. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/rafter_cli/resources/windsurf-rules/rafter-skill-review.md +0 -0
  88. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/rafter_cli/resources/windsurf-rules/rafter.md +0 -0
  89. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/rafter_cli/scanners/__init__.py +0 -0
  90. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/rafter_cli/scanners/betterleaks.py +0 -0
  91. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/rafter_cli/scanners/git_diff_scan.py +0 -0
  92. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/rafter_cli/scanners/git_history_scan.py +0 -0
  93. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/rafter_cli/scanners/regex_scanner.py +0 -0
  94. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/rafter_cli/scanners/skill_scanner.py +0 -0
  95. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/rafter_cli/scanners/union.py +0 -0
  96. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/rafter_cli/utils/__init__.py +0 -0
  97. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/rafter_cli/utils/api.py +0 -0
  98. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/rafter_cli/utils/binary_manager.py +0 -0
  99. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/rafter_cli/utils/formatter.py +0 -0
  100. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/rafter_cli/utils/git.py +0 -0
  101. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/rafter_cli/utils/git_diff.py +0 -0
  102. {rafter_cli-0.11.1 → rafter_cli-0.11.2}/rafter_cli/utils/skill_manager.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rafter-cli
3
- Version: 0.11.1
3
+ Version: 0.11.2
4
4
  Summary: Rafter CLI — the default security agent for AI workflows. Free for individuals and open source.
5
5
  License: MIT
6
6
  Author: Rafter Team
@@ -20,7 +20,7 @@ Requires-Dist: python-dotenv (>=1.2.2,<2.0.0)
20
20
  Requires-Dist: pyyaml (>=6.0.1,<7.0.0)
21
21
  Requires-Dist: requests (>=2.33.0,<3.0.0)
22
22
  Requires-Dist: rich (>=13.7.1,<14.0.0)
23
- Requires-Dist: typer (>=0.15.0,<0.16.0)
23
+ Requires-Dist: typer (>=0.16)
24
24
  Requires-Dist: urllib3 (>=2.7.0,<3)
25
25
  Requires-Dist: watchdog (>=4.0.0,<5.0.0)
26
26
  Description-Content-Type: text/markdown
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "rafter-cli"
3
- version = "0.11.1"
3
+ version = "0.11.2"
4
4
  description = "Rafter CLI — the default security agent for AI workflows. Free for individuals and open source."
5
5
  authors = ["Rafter Team <hello@rafter.so>"]
6
6
  license = "MIT"
@@ -8,7 +8,9 @@ readme = "README.md"
8
8
 
9
9
  [tool.poetry.dependencies]
10
10
  python = ">=3.11,<4.0"
11
- typer = "^0.15.0"
11
+ # typer >=0.16 supports click 8.2+. The old ^0.15 floor capped click <8.2, so a
12
+ # shared venv that pulled click 8.2+ broke --help (Parameter.make_metavar(ctx)).
13
+ typer = ">=0.16"
12
14
  click = ">=8.0.0,<9.0.0"
13
15
  rich = "^13.7.1"
14
16
  python-dotenv = "^1.2.2"
@@ -2130,6 +2130,11 @@ def _output_scan_results(
2130
2130
  # that could not complete cleanly (2); a clean, complete scan is 0.
2131
2131
  scan_exit_code = 1 if kept_results else (2 if read_errors else 0)
2132
2132
 
2133
+ # Before the format branches, so --json and SARIF runs are recorded too. A
2134
+ # clean scan is recorded as well: "we looked on this date and found nothing"
2135
+ # is what a postmortem needs.
2136
+ _log_scan_to_audit(kept_results, context)
2137
+
2133
2138
  # No silent downgrade (sable-jiqb): when betterleaks was requested/auto but
2134
2139
  # patterns ran instead, say so on stderr (visible in every format without
2135
2140
  # corrupting a JSON/SARIF stdout payload). The JSON body also carries it.
@@ -2234,6 +2239,48 @@ def _output_scan_results(
2234
2239
  raise typer.Exit(code=scan_exit_code)
2235
2240
 
2236
2241
 
2242
+ def _log_scan_to_audit(results: list[ScanResult], context: str | None = None) -> None:
2243
+ """Record that a scan ran, so ``rafter agent audit`` has something in it.
2244
+
2245
+ ``rafter secrets`` told the user "Run 'rafter agent audit' to see the
2246
+ security log" and the log was always empty, because only command
2247
+ interception ever wrote to it — three of seven beta users followed that
2248
+ hint and hit a dead end, one of them immediately after a correct and useful
2249
+ scan result. The first thing a careful person does with a security finding
2250
+ is look for the record of it.
2251
+
2252
+ Counts and pattern NAMES only — never a matched value, not even redacted.
2253
+ The audit log is a postmortem artifact that outlives the terminal, so it
2254
+ must not become a second place secrets live.
2255
+ """
2256
+ try:
2257
+ total_matches = sum(len(r.matches) for r in results)
2258
+ patterns = sorted({m.pattern.name for r in results for m in r.matches})
2259
+ details = {
2260
+ "filesWithFindings": len(results),
2261
+ "totalMatches": total_matches,
2262
+ "patterns": patterns,
2263
+ }
2264
+ if context:
2265
+ details["context"] = context
2266
+ AuditLogger().log({
2267
+ "eventType": "scan_executed",
2268
+ "securityCheck": {
2269
+ "passed": total_matches == 0,
2270
+ "reason": (
2271
+ "Secret scan completed with no findings"
2272
+ if total_matches == 0
2273
+ else f"Secret scan found {total_matches} secret(s) in {len(results)} file(s)"
2274
+ ),
2275
+ "details": details,
2276
+ },
2277
+ "resolution": {"actionTaken": "allowed" if total_matches == 0 else "blocked"},
2278
+ })
2279
+ except Exception:
2280
+ # Never let audit logging break a scan — the scan result is the product.
2281
+ pass
2282
+
2283
+
2237
2284
  def _watch_and_scan(
2238
2285
  watch_path: str,
2239
2286
  engine: str,
@@ -509,8 +509,15 @@ def _cursor_instructions() -> ComponentSpec:
509
509
  file_path = detect_dir / "rules" / "rafter-security.mdc"
510
510
 
511
511
  def uninstall() -> None:
512
- if file_path.exists():
513
- file_path.unlink()
512
+ # Enable injects a marker block (coexists with user content), so disable
513
+ # must excise only that block, not unlink the whole file. Remove the file
514
+ # only when it was ours alone; a file with no markers is user-authored.
515
+ if file_path.exists() and _strip_marker_block(file_path):
516
+ try:
517
+ if not file_path.read_text(encoding="utf-8").strip():
518
+ file_path.unlink()
519
+ except OSError:
520
+ pass
514
521
 
515
522
  return ComponentSpec(
516
523
  id="cursor.instructions",
@@ -1046,9 +1053,13 @@ def _aider_read() -> ComponentSpec:
1046
1053
  config_path.write_text(yaml.safe_dump(parsed, sort_keys=False), encoding="utf-8")
1047
1054
 
1048
1055
  def uninstall() -> None:
1049
- if rafter_md_path.exists():
1056
+ # Excise only the managed block; any user content in RAFTER.md survives.
1057
+ # If the file was ours alone (nothing but the block), remove it.
1058
+ # A RAFTER.md with no markers is user-authored — leave it untouched.
1059
+ if rafter_md_path.exists() and _strip_marker_block(rafter_md_path):
1050
1060
  try:
1051
- rafter_md_path.unlink()
1061
+ if not rafter_md_path.read_text(encoding="utf-8").strip():
1062
+ rafter_md_path.unlink()
1052
1063
  except OSError:
1053
1064
  pass
1054
1065
  if not config_path.exists():
@@ -639,6 +639,13 @@ def posttool(
639
639
  redacted["content"] = scanner.redact(content_text)
640
640
  modified = True
641
641
 
642
+ # Scan and redact the error field (sable-t578). A tool that fails and
643
+ # echoes a credential in its error text surfaces it like output.
644
+ error_text = tool_response.get("error", "")
645
+ if error_text and isinstance(error_text, str) and scanner.has_secrets(error_text):
646
+ redacted["error"] = scanner.redact(error_text)
647
+ modified = True
648
+
642
649
  if modified:
643
650
  audit = AuditLogger()
644
651
  match_count = 0
@@ -646,6 +653,8 @@ def posttool(
646
653
  match_count += len(scanner.scan_text(output_text))
647
654
  if content_text and isinstance(content_text, str):
648
655
  match_count += len(scanner.scan_text(content_text))
656
+ if error_text and isinstance(error_text, str):
657
+ match_count += len(scanner.scan_text(error_text))
649
658
  audit.log_content_sanitized(f"{tool_name} tool response", match_count)
650
659
  _write_posttool_output({"action": "modify", "tool_response": redacted}, format)
651
660
  return
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: rafter-security
3
3
  description: Security toolkit for AI workflows. Use when scanning code or repos for vulnerabilities, auditing third-party skills/MCPs/agent configs before installing, evaluating shell commands before running them, or generating secure design questions for new features. Provides `rafter run` (remote SAST + SCA, needs RAFTER_API_KEY), `rafter secrets` (offline secrets-only), `rafter agent exec --dry-run` (command-risk classification), and `rafter skill review`.
4
- version: 0.11.1
4
+ version: 0.11.2
5
5
  homepage: https://rafter.so
6
6
  metadata:
7
7
  openclaw:
@@ -9,7 +9,7 @@ DEFAULT_SECRET_PATTERNS: list[Pattern] = [
9
9
  name="AWS Access Key ID",
10
10
  # Negative lookahead excludes the canonical AWS-docs example key
11
11
  # (AKIAIOSFODNN7EXAMPLE and its siblings, which all end in EXAMPLE).
12
- # betterleaks skips these too (sable/#230); matching that keeps the
12
+ # betterleaks skips these too (#230); matching that keeps the
13
13
  # two engines from disagreeing on a well-known public non-secret.
14
14
  regex=r"(A3T[A-Z0-9]|AKIA|AGPA|AIDA|AROA|AIPA|ANPA|ANVA|ASIA)(?![A-Z0-9]{9}EXAMPLE)[A-Z0-9]{16}",
15
15
  severity="critical",
@@ -17,7 +17,12 @@ DEFAULT_SECRET_PATTERNS: list[Pattern] = [
17
17
  ),
18
18
  Pattern(
19
19
  name="AWS Secret Access Key",
20
- regex=r"(?i)aws(.{0,20})?['\"]?[0-9a-zA-Z/+]{40}['\"]?",
20
+ # Negative lookahead excludes the canonical AWS-docs example secret
21
+ # key (wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY, which ends in
22
+ # EXAMPLEKEY). betterleaks skips this too (#230); matching that
23
+ # keeps the two engines from disagreeing on a well-known public
24
+ # non-secret, mirroring the AWS Access Key ID exemption above.
25
+ regex=r"(?i)aws(.{0,20})?['\"]?(?![0-9a-zA-Z/+]{30}EXAMPLEKEY)[0-9a-zA-Z/+]{40}['\"]?",
21
26
  severity="critical",
22
27
  description="AWS Secret Access Key detected",
23
28
  ),
File without changes