rafter-cli 0.8.0__tar.gz → 0.8.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 (94) hide show
  1. {rafter_cli-0.8.0 → rafter_cli-0.8.2}/PKG-INFO +1 -1
  2. {rafter_cli-0.8.0 → rafter_cli-0.8.2}/pyproject.toml +1 -1
  3. {rafter_cli-0.8.0 → rafter_cli-0.8.2}/rafter_cli/commands/agent.py +12 -4
  4. {rafter_cli-0.8.0 → rafter_cli-0.8.2}/rafter_cli/commands/scan.py +4 -1
  5. {rafter_cli-0.8.0 → rafter_cli-0.8.2}/rafter_cli/resources/rafter-security-skill.md +2 -2
  6. {rafter_cli-0.8.0 → rafter_cli-0.8.2}/rafter_cli/scanners/regex_scanner.py +54 -0
  7. {rafter_cli-0.8.0 → rafter_cli-0.8.2}/README.md +0 -0
  8. {rafter_cli-0.8.0 → rafter_cli-0.8.2}/rafter_cli/__init__.py +0 -0
  9. {rafter_cli-0.8.0 → rafter_cli-0.8.2}/rafter_cli/__main__.py +0 -0
  10. {rafter_cli-0.8.0 → rafter_cli-0.8.2}/rafter_cli/commands/__init__.py +0 -0
  11. {rafter_cli-0.8.0 → rafter_cli-0.8.2}/rafter_cli/commands/agent_components.py +0 -0
  12. {rafter_cli-0.8.0 → rafter_cli-0.8.2}/rafter_cli/commands/backend.py +0 -0
  13. {rafter_cli-0.8.0 → rafter_cli-0.8.2}/rafter_cli/commands/brief.py +0 -0
  14. {rafter_cli-0.8.0 → rafter_cli-0.8.2}/rafter_cli/commands/ci.py +0 -0
  15. {rafter_cli-0.8.0 → rafter_cli-0.8.2}/rafter_cli/commands/docs.py +0 -0
  16. {rafter_cli-0.8.0 → rafter_cli-0.8.2}/rafter_cli/commands/hook.py +0 -0
  17. {rafter_cli-0.8.0 → rafter_cli-0.8.2}/rafter_cli/commands/issues/__init__.py +0 -0
  18. {rafter_cli-0.8.0 → rafter_cli-0.8.2}/rafter_cli/commands/issues/dedup.py +0 -0
  19. {rafter_cli-0.8.0 → rafter_cli-0.8.2}/rafter_cli/commands/issues/github_client.py +0 -0
  20. {rafter_cli-0.8.0 → rafter_cli-0.8.2}/rafter_cli/commands/issues/issue_builder.py +0 -0
  21. {rafter_cli-0.8.0 → rafter_cli-0.8.2}/rafter_cli/commands/issues/issues_app.py +0 -0
  22. {rafter_cli-0.8.0 → rafter_cli-0.8.2}/rafter_cli/commands/mcp_server.py +0 -0
  23. {rafter_cli-0.8.0 → rafter_cli-0.8.2}/rafter_cli/commands/notify.py +0 -0
  24. {rafter_cli-0.8.0 → rafter_cli-0.8.2}/rafter_cli/commands/policy.py +0 -0
  25. {rafter_cli-0.8.0 → rafter_cli-0.8.2}/rafter_cli/commands/report.py +0 -0
  26. {rafter_cli-0.8.0 → rafter_cli-0.8.2}/rafter_cli/commands/skill.py +0 -0
  27. {rafter_cli-0.8.0 → rafter_cli-0.8.2}/rafter_cli/commands/skill_remote.py +0 -0
  28. {rafter_cli-0.8.0 → rafter_cli-0.8.2}/rafter_cli/core/__init__.py +0 -0
  29. {rafter_cli-0.8.0 → rafter_cli-0.8.2}/rafter_cli/core/audit_logger.py +0 -0
  30. {rafter_cli-0.8.0 → rafter_cli-0.8.2}/rafter_cli/core/command_interceptor.py +0 -0
  31. {rafter_cli-0.8.0 → rafter_cli-0.8.2}/rafter_cli/core/config_manager.py +0 -0
  32. {rafter_cli-0.8.0 → rafter_cli-0.8.2}/rafter_cli/core/config_schema.py +0 -0
  33. {rafter_cli-0.8.0 → rafter_cli-0.8.2}/rafter_cli/core/custom_patterns.py +0 -0
  34. {rafter_cli-0.8.0 → rafter_cli-0.8.2}/rafter_cli/core/docs_loader.py +0 -0
  35. {rafter_cli-0.8.0 → rafter_cli-0.8.2}/rafter_cli/core/pattern_engine.py +0 -0
  36. {rafter_cli-0.8.0 → rafter_cli-0.8.2}/rafter_cli/core/policy_loader.py +0 -0
  37. {rafter_cli-0.8.0 → rafter_cli-0.8.2}/rafter_cli/core/risk_rules.py +0 -0
  38. {rafter_cli-0.8.0 → rafter_cli-0.8.2}/rafter_cli/resources/__init__.py +0 -0
  39. {rafter_cli-0.8.0 → rafter_cli-0.8.2}/rafter_cli/resources/agents/__init__.py +0 -0
  40. {rafter_cli-0.8.0 → rafter_cli-0.8.2}/rafter_cli/resources/agents/rafter.md +0 -0
  41. {rafter_cli-0.8.0 → rafter_cli-0.8.2}/rafter_cli/resources/continue-rules/rafter-code-review.md +0 -0
  42. {rafter_cli-0.8.0 → rafter_cli-0.8.2}/rafter_cli/resources/continue-rules/rafter-secure-design.md +0 -0
  43. {rafter_cli-0.8.0 → rafter_cli-0.8.2}/rafter_cli/resources/continue-rules/rafter-skill-review.md +0 -0
  44. {rafter_cli-0.8.0 → rafter_cli-0.8.2}/rafter_cli/resources/continue-rules/rafter.md +0 -0
  45. {rafter_cli-0.8.0 → rafter_cli-0.8.2}/rafter_cli/resources/cursor-rules/rafter-code-review.mdc +0 -0
  46. {rafter_cli-0.8.0 → rafter_cli-0.8.2}/rafter_cli/resources/cursor-rules/rafter-secure-design.mdc +0 -0
  47. {rafter_cli-0.8.0 → rafter_cli-0.8.2}/rafter_cli/resources/cursor-rules/rafter-skill-review.mdc +0 -0
  48. {rafter_cli-0.8.0 → rafter_cli-0.8.2}/rafter_cli/resources/cursor-rules/rafter.mdc +0 -0
  49. {rafter_cli-0.8.0 → rafter_cli-0.8.2}/rafter_cli/resources/pre-commit-hook.sh +0 -0
  50. {rafter_cli-0.8.0 → rafter_cli-0.8.2}/rafter_cli/resources/pre-push-hook.sh +0 -0
  51. {rafter_cli-0.8.0 → rafter_cli-0.8.2}/rafter_cli/resources/skills/__init__.py +0 -0
  52. {rafter_cli-0.8.0 → rafter_cli-0.8.2}/rafter_cli/resources/skills/rafter/SKILL.md +0 -0
  53. {rafter_cli-0.8.0 → rafter_cli-0.8.2}/rafter_cli/resources/skills/rafter/__init__.py +0 -0
  54. {rafter_cli-0.8.0 → rafter_cli-0.8.2}/rafter_cli/resources/skills/rafter/docs/backend.md +0 -0
  55. {rafter_cli-0.8.0 → rafter_cli-0.8.2}/rafter_cli/resources/skills/rafter/docs/cli-reference.md +0 -0
  56. {rafter_cli-0.8.0 → rafter_cli-0.8.2}/rafter_cli/resources/skills/rafter/docs/finding-triage.md +0 -0
  57. {rafter_cli-0.8.0 → rafter_cli-0.8.2}/rafter_cli/resources/skills/rafter/docs/guardrails.md +0 -0
  58. {rafter_cli-0.8.0 → rafter_cli-0.8.2}/rafter_cli/resources/skills/rafter/docs/shift-left.md +0 -0
  59. {rafter_cli-0.8.0 → rafter_cli-0.8.2}/rafter_cli/resources/skills/rafter-code-review/SKILL.md +0 -0
  60. {rafter_cli-0.8.0 → rafter_cli-0.8.2}/rafter_cli/resources/skills/rafter-code-review/docs/api.md +0 -0
  61. {rafter_cli-0.8.0 → rafter_cli-0.8.2}/rafter_cli/resources/skills/rafter-code-review/docs/asvs.md +0 -0
  62. {rafter_cli-0.8.0 → rafter_cli-0.8.2}/rafter_cli/resources/skills/rafter-code-review/docs/cwe-top25.md +0 -0
  63. {rafter_cli-0.8.0 → rafter_cli-0.8.2}/rafter_cli/resources/skills/rafter-code-review/docs/investigation-playbook.md +0 -0
  64. {rafter_cli-0.8.0 → rafter_cli-0.8.2}/rafter_cli/resources/skills/rafter-code-review/docs/llm.md +0 -0
  65. {rafter_cli-0.8.0 → rafter_cli-0.8.2}/rafter_cli/resources/skills/rafter-code-review/docs/web-app.md +0 -0
  66. {rafter_cli-0.8.0 → rafter_cli-0.8.2}/rafter_cli/resources/skills/rafter-secure-design/SKILL.md +0 -0
  67. {rafter_cli-0.8.0 → rafter_cli-0.8.2}/rafter_cli/resources/skills/rafter-secure-design/docs/api-design.md +0 -0
  68. {rafter_cli-0.8.0 → rafter_cli-0.8.2}/rafter_cli/resources/skills/rafter-secure-design/docs/auth.md +0 -0
  69. {rafter_cli-0.8.0 → rafter_cli-0.8.2}/rafter_cli/resources/skills/rafter-secure-design/docs/data-storage.md +0 -0
  70. {rafter_cli-0.8.0 → rafter_cli-0.8.2}/rafter_cli/resources/skills/rafter-secure-design/docs/dependencies.md +0 -0
  71. {rafter_cli-0.8.0 → rafter_cli-0.8.2}/rafter_cli/resources/skills/rafter-secure-design/docs/deployment.md +0 -0
  72. {rafter_cli-0.8.0 → rafter_cli-0.8.2}/rafter_cli/resources/skills/rafter-secure-design/docs/ingestion.md +0 -0
  73. {rafter_cli-0.8.0 → rafter_cli-0.8.2}/rafter_cli/resources/skills/rafter-secure-design/docs/standards-pointers.md +0 -0
  74. {rafter_cli-0.8.0 → rafter_cli-0.8.2}/rafter_cli/resources/skills/rafter-secure-design/docs/threat-modeling.md +0 -0
  75. {rafter_cli-0.8.0 → rafter_cli-0.8.2}/rafter_cli/resources/skills/rafter-skill-review/SKILL.md +0 -0
  76. {rafter_cli-0.8.0 → rafter_cli-0.8.2}/rafter_cli/resources/skills/rafter-skill-review/docs/authorship-provenance.md +0 -0
  77. {rafter_cli-0.8.0 → rafter_cli-0.8.2}/rafter_cli/resources/skills/rafter-skill-review/docs/changelog-review.md +0 -0
  78. {rafter_cli-0.8.0 → rafter_cli-0.8.2}/rafter_cli/resources/skills/rafter-skill-review/docs/data-practices.md +0 -0
  79. {rafter_cli-0.8.0 → rafter_cli-0.8.2}/rafter_cli/resources/skills/rafter-skill-review/docs/malware-indicators.md +0 -0
  80. {rafter_cli-0.8.0 → rafter_cli-0.8.2}/rafter_cli/resources/skills/rafter-skill-review/docs/prompt-injection.md +0 -0
  81. {rafter_cli-0.8.0 → rafter_cli-0.8.2}/rafter_cli/resources/skills/rafter-skill-review/docs/telemetry.md +0 -0
  82. {rafter_cli-0.8.0 → rafter_cli-0.8.2}/rafter_cli/resources/windsurf-rules/rafter-code-review.md +0 -0
  83. {rafter_cli-0.8.0 → rafter_cli-0.8.2}/rafter_cli/resources/windsurf-rules/rafter-secure-design.md +0 -0
  84. {rafter_cli-0.8.0 → rafter_cli-0.8.2}/rafter_cli/resources/windsurf-rules/rafter-skill-review.md +0 -0
  85. {rafter_cli-0.8.0 → rafter_cli-0.8.2}/rafter_cli/resources/windsurf-rules/rafter.md +0 -0
  86. {rafter_cli-0.8.0 → rafter_cli-0.8.2}/rafter_cli/scanners/__init__.py +0 -0
  87. {rafter_cli-0.8.0 → rafter_cli-0.8.2}/rafter_cli/scanners/betterleaks.py +0 -0
  88. {rafter_cli-0.8.0 → rafter_cli-0.8.2}/rafter_cli/scanners/secret_patterns.py +0 -0
  89. {rafter_cli-0.8.0 → rafter_cli-0.8.2}/rafter_cli/utils/__init__.py +0 -0
  90. {rafter_cli-0.8.0 → rafter_cli-0.8.2}/rafter_cli/utils/api.py +0 -0
  91. {rafter_cli-0.8.0 → rafter_cli-0.8.2}/rafter_cli/utils/binary_manager.py +0 -0
  92. {rafter_cli-0.8.0 → rafter_cli-0.8.2}/rafter_cli/utils/formatter.py +0 -0
  93. {rafter_cli-0.8.0 → rafter_cli-0.8.2}/rafter_cli/utils/git.py +0 -0
  94. {rafter_cli-0.8.0 → rafter_cli-0.8.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.8.0
3
+ Version: 0.8.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
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "rafter-cli"
3
- version = "0.8.0"
3
+ version = "0.8.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"
@@ -1424,7 +1424,14 @@ def _scan_file(file_path: str, engine: str, custom_patterns=None) -> list[ScanRe
1424
1424
  return [r] if r.matches else []
1425
1425
 
1426
1426
 
1427
- def _scan_directory(dir_path: str, engine: str, scan_cfg=None, *, history: bool = False) -> list[ScanResult]:
1427
+ def _scan_directory(
1428
+ dir_path: str,
1429
+ engine: str,
1430
+ scan_cfg=None,
1431
+ *,
1432
+ history: bool = False,
1433
+ respect_gitignore: bool = True,
1434
+ ) -> list[ScanResult]:
1428
1435
  custom = None
1429
1436
  exclude = None
1430
1437
  if scan_cfg:
@@ -1438,10 +1445,10 @@ def _scan_directory(dir_path: str, engine: str, scan_cfg=None, *, history: bool
1438
1445
  return [ScanResult(file=r.file, matches=r.matches) for r in results]
1439
1446
  except Exception:
1440
1447
  scanner = RegexScanner(custom)
1441
- return scanner.scan_directory(dir_path, exclude_paths=exclude)
1448
+ return scanner.scan_directory(dir_path, exclude_paths=exclude, respect_gitignore=respect_gitignore)
1442
1449
  else:
1443
1450
  scanner = RegexScanner(custom)
1444
- return scanner.scan_directory(dir_path, exclude_paths=exclude)
1451
+ return scanner.scan_directory(dir_path, exclude_paths=exclude, respect_gitignore=respect_gitignore)
1445
1452
 
1446
1453
 
1447
1454
  def _output_scan_results(
@@ -1674,6 +1681,7 @@ def scan(
1674
1681
  baseline: bool = typer.Option(False, "--baseline", help="Filter findings present in the saved baseline"),
1675
1682
  watch: bool = typer.Option(False, "--watch", help="Watch for file changes and re-scan on change"),
1676
1683
  history: bool = typer.Option(False, "--history", help="Scan git history for secrets (requires betterleaks engine)"),
1684
+ gitignore: bool = typer.Option(True, "--gitignore/--no-gitignore", help="Respect .gitignore when walking the scan target (default: on)"),
1677
1685
  ):
1678
1686
  """Scan files or directories for secrets. [deprecated: use 'rafter secrets' instead]"""
1679
1687
  print(
@@ -1771,7 +1779,7 @@ def scan(
1771
1779
  if os.path.isdir(resolved_path):
1772
1780
  if not quiet:
1773
1781
  print(f"Scanning directory: {resolved_path} ({eng})", file=sys.stderr)
1774
- results = _scan_directory(resolved_path, eng, scan_cfg, history=history)
1782
+ results = _scan_directory(resolved_path, eng, scan_cfg, history=history, respect_gitignore=gitignore)
1775
1783
  else:
1776
1784
  if not quiet:
1777
1785
  print(f"Scanning file: {resolved_path} ({eng})", file=sys.stderr)
@@ -96,6 +96,7 @@ def scan_local(
96
96
  baseline: bool = typer.Option(False, "--baseline", help="Filter findings present in the saved baseline"),
97
97
  watch: bool = typer.Option(False, "--watch", help="Watch for file changes and re-scan on change"),
98
98
  history: bool = typer.Option(False, "--history", help="Scan git history for secrets (requires betterleaks engine)"),
99
+ gitignore: bool = typer.Option(True, "--gitignore/--no-gitignore", help="Respect .gitignore when walking the scan target (default: on)"),
99
100
  ):
100
101
  """(deprecated alias for 'rafter secrets')."""
101
102
  from .agent import (
@@ -220,7 +221,7 @@ def scan_local(
220
221
  if os.path.isdir(resolved_path):
221
222
  if not quiet:
222
223
  print(f"Scanning directory: {resolved_path} ({eng})", file=sys.stderr)
223
- results = _scan_directory(resolved_path, eng, scan_cfg, history=history)
224
+ results = _scan_directory(resolved_path, eng, scan_cfg, history=history, respect_gitignore=gitignore)
224
225
  else:
225
226
  if not quiet:
226
227
  print(f"Scanning file: {resolved_path} ({eng})", file=sys.stderr)
@@ -256,6 +257,7 @@ def secrets(
256
257
  baseline: bool = typer.Option(False, "--baseline", help="Filter findings present in the saved baseline"),
257
258
  watch: bool = typer.Option(False, "--watch", help="Watch for file changes and re-scan on change"),
258
259
  history: bool = typer.Option(False, "--history", help="Scan git history for secrets (requires betterleaks engine)"),
260
+ gitignore: bool = typer.Option(True, "--gitignore/--no-gitignore", help="Respect .gitignore when walking the scan target (default: on)"),
259
261
  ):
260
262
  """Scan files/directories for hardcoded secrets."""
261
263
  return scan_local(
@@ -269,4 +271,5 @@ def secrets(
269
271
  baseline=baseline,
270
272
  watch=watch,
271
273
  history=history,
274
+ gitignore=gitignore,
272
275
  )
@@ -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.8.0
4
+ version: 0.8.2
5
5
  homepage: https://rafter.so
6
6
  metadata:
7
7
  openclaw:
@@ -15,7 +15,7 @@ metadata:
15
15
  - name: RAFTER_API_KEY
16
16
  required: false
17
17
  description: API key for `rafter run` (remote SAST + SCA + agentic deep-dive). Without it, `rafter secrets` (local secrets scan) still works.
18
- last_updated: 2026-05-07
18
+ last_updated: 2026-05-12
19
19
  ---
20
20
 
21
21
  # Rafter Security
@@ -2,6 +2,7 @@
2
2
  from __future__ import annotations
3
3
 
4
4
  import os
5
+ import subprocess
5
6
  from dataclasses import dataclass, field
6
7
  from pathlib import Path
7
8
 
@@ -67,6 +68,7 @@ class RegexScanner:
67
68
  dir_path: str,
68
69
  exclude_paths: list[str] | None = None,
69
70
  max_depth: int = 10,
71
+ respect_gitignore: bool = True,
70
72
  ) -> list[ScanResult]:
71
73
  exclude = list(DEFAULT_EXCLUDE)
72
74
  if exclude_paths:
@@ -76,6 +78,8 @@ class RegexScanner:
76
78
  exclude.append(cleaned)
77
79
 
78
80
  files = self._walk(dir_path, exclude, max_depth)
81
+ if respect_gitignore:
82
+ files = _filter_gitignored(files, dir_path)
79
83
  return self.scan_files(files)
80
84
 
81
85
  def scan_text(self, text: str) -> list[PatternMatch]:
@@ -110,3 +114,53 @@ class RegexScanner:
110
114
  except PermissionError:
111
115
  pass
112
116
  return files
117
+
118
+
119
+ def _filter_gitignored(files: list[str], scan_root: str) -> list[str]:
120
+ """Drop files git would ignore relative to ``scan_root``.
121
+
122
+ Shells out to ``git check-ignore --stdin --no-index -z`` inside the scan
123
+ root's work tree so every gitignore semantic git supports (nested
124
+ .gitignores, negations, .git/info/exclude, the configured global excludes
125
+ file, the full pattern grammar) is honored exactly. Zero new
126
+ dependencies. Bails out silently — returning the input unchanged — when
127
+ git is missing or the scan root sits outside a work tree.
128
+ """
129
+ if not files:
130
+ return files
131
+ # Locate the work tree that owns scan_root.
132
+ try:
133
+ probe = subprocess.run(
134
+ ["git", "-C", scan_root, "rev-parse", "--show-toplevel"],
135
+ capture_output=True,
136
+ text=True,
137
+ timeout=5,
138
+ )
139
+ except (FileNotFoundError, subprocess.TimeoutExpired):
140
+ return files
141
+ if probe.returncode != 0:
142
+ return files
143
+ work_tree = probe.stdout.strip()
144
+ if not work_tree:
145
+ return files
146
+
147
+ # Batch every candidate through one `git check-ignore --stdin` call.
148
+ # Null-delimited I/O preserves paths containing newlines / spaces.
149
+ stdin = ("\0".join(files) + "\0").encode("utf-8")
150
+ try:
151
+ result = subprocess.run(
152
+ ["git", "-C", work_tree, "check-ignore", "--stdin", "--no-index", "-z"],
153
+ input=stdin,
154
+ capture_output=True,
155
+ timeout=30,
156
+ )
157
+ except (FileNotFoundError, subprocess.TimeoutExpired):
158
+ return files
159
+ # Exit codes: 0 = at least one path ignored; 1 = none; 128 = error.
160
+ # Anything else => abandon the filter rather than break the scan.
161
+ if result.returncode not in (0, 1):
162
+ return files
163
+ ignored = {p.decode("utf-8") for p in result.stdout.split(b"\0") if p}
164
+ if not ignored:
165
+ return files
166
+ return [f for f in files if f not in ignored]
File without changes