rafter-cli 0.10.1__tar.gz → 0.10.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 (101) hide show
  1. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/PKG-INFO +1 -1
  2. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/pyproject.toml +1 -1
  3. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/rafter_cli/core/config_manager.py +111 -6
  4. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/rafter_cli/core/config_schema.py +7 -0
  5. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/rafter_cli/resources/rafter-security-skill.md +1 -1
  6. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/README.md +0 -0
  7. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/rafter_cli/__init__.py +0 -0
  8. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/rafter_cli/__main__.py +0 -0
  9. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/rafter_cli/commands/__init__.py +0 -0
  10. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/rafter_cli/commands/agent.py +0 -0
  11. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/rafter_cli/commands/agent_components.py +0 -0
  12. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/rafter_cli/commands/backend.py +0 -0
  13. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/rafter_cli/commands/brief.py +0 -0
  14. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/rafter_cli/commands/ci.py +0 -0
  15. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/rafter_cli/commands/docs.py +0 -0
  16. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/rafter_cli/commands/hook.py +0 -0
  17. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/rafter_cli/commands/issues/__init__.py +0 -0
  18. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/rafter_cli/commands/issues/dedup.py +0 -0
  19. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/rafter_cli/commands/issues/github_client.py +0 -0
  20. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/rafter_cli/commands/issues/issue_builder.py +0 -0
  21. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/rafter_cli/commands/issues/issues_app.py +0 -0
  22. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/rafter_cli/commands/mcp_server.py +0 -0
  23. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/rafter_cli/commands/notify.py +0 -0
  24. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/rafter_cli/commands/policy.py +0 -0
  25. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/rafter_cli/commands/report.py +0 -0
  26. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/rafter_cli/commands/scan.py +0 -0
  27. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/rafter_cli/commands/sites.py +0 -0
  28. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/rafter_cli/commands/skill.py +0 -0
  29. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/rafter_cli/commands/skill_remote.py +0 -0
  30. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/rafter_cli/core/__init__.py +0 -0
  31. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/rafter_cli/core/audit_logger.py +0 -0
  32. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/rafter_cli/core/command_interceptor.py +0 -0
  33. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/rafter_cli/core/custom_patterns.py +0 -0
  34. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/rafter_cli/core/docs_loader.py +0 -0
  35. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/rafter_cli/core/hook_control.py +0 -0
  36. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/rafter_cli/core/pattern_engine.py +0 -0
  37. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/rafter_cli/core/policy_loader.py +0 -0
  38. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/rafter_cli/core/risk_rules.py +0 -0
  39. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/rafter_cli/core/suppression_writer.py +0 -0
  40. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/rafter_cli/resources/__init__.py +0 -0
  41. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/rafter_cli/resources/agents/__init__.py +0 -0
  42. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/rafter_cli/resources/agents/rafter.md +0 -0
  43. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/rafter_cli/resources/continue-rules/rafter-code-review.md +0 -0
  44. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/rafter_cli/resources/continue-rules/rafter-secure-design.md +0 -0
  45. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/rafter_cli/resources/continue-rules/rafter-skill-review.md +0 -0
  46. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/rafter_cli/resources/continue-rules/rafter.md +0 -0
  47. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/rafter_cli/resources/cursor-rules/rafter-code-review.mdc +0 -0
  48. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/rafter_cli/resources/cursor-rules/rafter-secure-design.mdc +0 -0
  49. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/rafter_cli/resources/cursor-rules/rafter-skill-review.mdc +0 -0
  50. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/rafter_cli/resources/cursor-rules/rafter.mdc +0 -0
  51. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/rafter_cli/resources/pre-commit-hook.sh +0 -0
  52. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/rafter_cli/resources/pre-push-hook.sh +0 -0
  53. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/rafter_cli/resources/skills/__init__.py +0 -0
  54. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/rafter_cli/resources/skills/rafter/SKILL.md +0 -0
  55. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/rafter_cli/resources/skills/rafter/__init__.py +0 -0
  56. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/rafter_cli/resources/skills/rafter/docs/backend.md +0 -0
  57. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/rafter_cli/resources/skills/rafter/docs/cli-reference.md +0 -0
  58. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/rafter_cli/resources/skills/rafter/docs/finding-triage.md +0 -0
  59. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/rafter_cli/resources/skills/rafter/docs/guardrails.md +0 -0
  60. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/rafter_cli/resources/skills/rafter/docs/shift-left.md +0 -0
  61. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/rafter_cli/resources/skills/rafter-code-review/SKILL.md +0 -0
  62. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/rafter_cli/resources/skills/rafter-code-review/docs/api.md +0 -0
  63. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/rafter_cli/resources/skills/rafter-code-review/docs/asvs.md +0 -0
  64. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/rafter_cli/resources/skills/rafter-code-review/docs/cwe-top25.md +0 -0
  65. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/rafter_cli/resources/skills/rafter-code-review/docs/investigation-playbook.md +0 -0
  66. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/rafter_cli/resources/skills/rafter-code-review/docs/llm.md +0 -0
  67. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/rafter_cli/resources/skills/rafter-code-review/docs/web-app.md +0 -0
  68. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/rafter_cli/resources/skills/rafter-secure-design/SKILL.md +0 -0
  69. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/rafter_cli/resources/skills/rafter-secure-design/docs/api-design.md +0 -0
  70. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/rafter_cli/resources/skills/rafter-secure-design/docs/auth.md +0 -0
  71. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/rafter_cli/resources/skills/rafter-secure-design/docs/data-storage.md +0 -0
  72. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/rafter_cli/resources/skills/rafter-secure-design/docs/dependencies.md +0 -0
  73. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/rafter_cli/resources/skills/rafter-secure-design/docs/deployment.md +0 -0
  74. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/rafter_cli/resources/skills/rafter-secure-design/docs/ingestion.md +0 -0
  75. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/rafter_cli/resources/skills/rafter-secure-design/docs/standards-pointers.md +0 -0
  76. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/rafter_cli/resources/skills/rafter-secure-design/docs/threat-modeling.md +0 -0
  77. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/rafter_cli/resources/skills/rafter-skill-review/SKILL.md +0 -0
  78. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/rafter_cli/resources/skills/rafter-skill-review/docs/authorship-provenance.md +0 -0
  79. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/rafter_cli/resources/skills/rafter-skill-review/docs/changelog-review.md +0 -0
  80. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/rafter_cli/resources/skills/rafter-skill-review/docs/data-practices.md +0 -0
  81. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/rafter_cli/resources/skills/rafter-skill-review/docs/malware-indicators.md +0 -0
  82. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/rafter_cli/resources/skills/rafter-skill-review/docs/prompt-injection.md +0 -0
  83. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/rafter_cli/resources/skills/rafter-skill-review/docs/telemetry.md +0 -0
  84. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/rafter_cli/resources/windsurf-rules/rafter-code-review.md +0 -0
  85. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/rafter_cli/resources/windsurf-rules/rafter-secure-design.md +0 -0
  86. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/rafter_cli/resources/windsurf-rules/rafter-skill-review.md +0 -0
  87. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/rafter_cli/resources/windsurf-rules/rafter.md +0 -0
  88. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/rafter_cli/scanners/__init__.py +0 -0
  89. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/rafter_cli/scanners/betterleaks.py +0 -0
  90. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/rafter_cli/scanners/git_diff_scan.py +0 -0
  91. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/rafter_cli/scanners/regex_scanner.py +0 -0
  92. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/rafter_cli/scanners/secret_patterns.py +0 -0
  93. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/rafter_cli/scanners/skill_scanner.py +0 -0
  94. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/rafter_cli/scanners/union.py +0 -0
  95. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/rafter_cli/utils/__init__.py +0 -0
  96. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/rafter_cli/utils/api.py +0 -0
  97. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/rafter_cli/utils/binary_manager.py +0 -0
  98. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/rafter_cli/utils/formatter.py +0 -0
  99. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/rafter_cli/utils/git.py +0 -0
  100. {rafter_cli-0.10.1 → rafter_cli-0.10.2}/rafter_cli/utils/git_diff.py +0 -0
  101. {rafter_cli-0.10.1 → rafter_cli-0.10.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.10.1
3
+ Version: 0.10.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.10.1"
3
+ version = "0.10.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"
@@ -206,6 +206,10 @@ class ConfigManager:
206
206
  if key in cp and (not isinstance(cp[key], list) or not all(isinstance(v, str) for v in cp[key])):
207
207
  print(f'rafter: config "commandPolicy.{key}" must be an array of strings — using default.', file=sys.stderr)
208
208
  del cp[key]
209
+ for key in ("allowProjectOverride", "allow_project_override"):
210
+ if key in cp and not isinstance(cp[key], bool):
211
+ print(f'rafter: config "commandPolicy.{key}" must be a boolean — ignoring (project policies cannot loosen command policy).', file=sys.stderr)
212
+ del cp[key]
209
213
 
210
214
  # audit
211
215
  audit = agent.get("audit")
@@ -269,12 +273,15 @@ class ConfigManager:
269
273
 
270
274
  cp = policy.get("command_policy")
271
275
  if cp:
272
- if cp.get("mode"):
273
- config.agent.command_policy.mode = cp["mode"]
274
- if cp.get("blocked_patterns") is not None:
275
- config.agent.command_policy.blocked_patterns = cp["blocked_patterns"]
276
- if cp.get("require_approval") is not None:
277
- config.agent.command_policy.require_approval = cp["require_approval"]
276
+ # The global config is a FLOOR the project may raise but never
277
+ # lower. See merge_command_policy (sable-nz4y). allow_project_override
278
+ # is read from self.load() — the GLOBAL config only, never from the
279
+ # project policy being merged.
280
+ merge_command_policy(
281
+ config.agent.command_policy,
282
+ cp,
283
+ self.load().agent.command_policy.allow_project_override is True,
284
+ )
278
285
 
279
286
  scan = policy.get("scan")
280
287
  if scan:
@@ -435,3 +442,101 @@ class ConfigManager:
435
442
  else:
436
443
  out[k] = v
437
444
  return out
445
+
446
+
447
+ # ---------------------------------------------------------------------------
448
+ # Project-policy floor (sable-nz4y)
449
+ # ---------------------------------------------------------------------------
450
+ #
451
+ # Policy discovery walks up from cwd to the git root, so the ``.rafter.yml`` this
452
+ # merges is a file IN THE REPOSITORY BEING WORKED ON. rafter ships inside agent
453
+ # pretool hooks, so on an untrusted repo that file is attacker-controlled.
454
+ #
455
+ # It used to REPLACE the machine owner's command policy wholesale: a repo
456
+ # shipping ``{mode: allow-all, blocked_patterns: [], require_approval: []}``
457
+ # switched off every guardrail below the unconditional critical hard-block —
458
+ # including patterns the owner had explicitly deny-listed.
459
+ #
460
+ # The rule this restores is the one the codebase already states ~20 lines above,
461
+ # for the Plus-approval gate (sable-9ddf): a project policy may turn a gate ON,
462
+ # but must never turn OFF a gate the machine owner set globally. So the global
463
+ # config is a FLOOR:
464
+ #
465
+ # * blocked_patterns / require_approval — UNION. A project adds rules; removing
466
+ # one the owner set is not expressible.
467
+ # * mode — accepted only when at least as strict as the owner's. A project may
468
+ # tighten ``allow-all`` to ``approve-dangerous``, never the reverse.
469
+ #
470
+ # The owner (never the repo) can opt out with
471
+ # ``agent.commandPolicy.allowProjectOverride: true`` in the GLOBAL config, which
472
+ # restores the old replace semantics for people who deliberately delegate policy
473
+ # to their projects.
474
+ #
475
+ # Note this does not touch the critical hard-block, which was never reachable
476
+ # from policy: CommandInterceptor.evaluate() returns on "critical" BEFORE any
477
+ # policy is loaded. That property held under every hostile policy tested; this
478
+ # change is about everything *below* critical.
479
+ #
480
+ # Mirrors ``mergeCommandPolicy`` in node/src/core/config-manager.ts.
481
+
482
+ #: Strictness rank for command-policy modes. Higher is stricter.
483
+ #:
484
+ #: ``approve-dangerous`` is the only mode that gates on assessed risk, so it is
485
+ #: strictly stronger than the other two. ``deny-list`` and ``allow-all``
486
+ #: currently behave identically in the interceptor (both rely solely on the
487
+ #: explicit pattern lists, which are checked regardless of mode) — they are
488
+ #: ranked apart anyway so that a project moving ``allow-all`` -> ``deny-list`` is
489
+ #: treated as a tightening rather than a lateral move if their behavior ever
490
+ #: diverges.
491
+ _COMMAND_MODE_STRICTNESS: dict[str, int] = {
492
+ "allow-all": 0,
493
+ "deny-list": 1,
494
+ "approve-dangerous": 2,
495
+ }
496
+
497
+
498
+ def _union_patterns(floor: list[str], project: list[str]) -> list[str]:
499
+ """Union preserving order: floor entries first, then new project entries."""
500
+ seen = set(floor)
501
+ return [*floor, *[p for p in project if p not in seen]]
502
+
503
+
504
+ def merge_command_policy(target, project: dict, allow_override: bool) -> None:
505
+ """Merge a project command policy over the global one under the floor rule.
506
+
507
+ Mutates ``target`` in place, matching the surrounding merge style. When
508
+ ``allow_override`` is True the pre-sable-nz4y replace semantics are used.
509
+ """
510
+ if allow_override:
511
+ if project.get("mode"):
512
+ target.mode = project["mode"]
513
+ if project.get("blocked_patterns") is not None:
514
+ target.blocked_patterns = project["blocked_patterns"]
515
+ if project.get("require_approval") is not None:
516
+ target.require_approval = project["require_approval"]
517
+ return
518
+
519
+ mode = project.get("mode")
520
+ if mode and mode != target.mode:
521
+ project_rank = _COMMAND_MODE_STRICTNESS.get(mode)
522
+ floor_rank = _COMMAND_MODE_STRICTNESS.get(target.mode)
523
+ # An unknown mode is not demonstrably at least as strict, so it is refused.
524
+ if project_rank is not None and floor_rank is not None and project_rank >= floor_rank:
525
+ target.mode = mode
526
+ else:
527
+ print(
528
+ f'rafter: project policy sets agent.commandPolicy.mode "{mode}", which is '
529
+ f'less strict than "{target.mode}" from your global config — ignoring. Set '
530
+ "agent.commandPolicy.allowProjectOverride: true in your global config to "
531
+ "allow project policies to loosen command policy.",
532
+ file=sys.stderr,
533
+ )
534
+
535
+ if project.get("blocked_patterns") is not None:
536
+ target.blocked_patterns = _union_patterns(
537
+ target.blocked_patterns, project["blocked_patterns"]
538
+ )
539
+ if project.get("require_approval") is not None:
540
+ target.require_approval = _union_patterns(
541
+ target.require_approval, project["require_approval"]
542
+ )
@@ -49,6 +49,13 @@ class CommandPolicyConfig:
49
49
  require_approval: list[str] = field(
50
50
  default_factory=lambda: list(_default_require_approval())
51
51
  )
52
+ #: Opt out of the project-policy floor (sable-nz4y).
53
+ #:
54
+ #: Read ONLY from the machine owner's global config — never from a project
55
+ #: ``.rafter.yml``, or a repo could grant itself the permission and the floor
56
+ #: would be no floor at all. Default (absent/False) keeps the floor: a
57
+ #: project policy may tighten command policy, never loosen it.
58
+ allow_project_override: bool = False
52
59
 
53
60
 
54
61
  @dataclass
@@ -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.10.1
4
+ version: 0.10.2
5
5
  homepage: https://rafter.so
6
6
  metadata:
7
7
  openclaw:
File without changes