@khoavu882/doflow 2.0.0-beta.3 → 2.0.0-beta.5
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.
- package/bin/doflow.js +2 -2
- package/core/.antigravity-plugin/plugin.json +0 -1
- package/core/.claude-plugin/plugin.json +1 -1
- package/core/.codex-plugin/plugin.json +1 -1
- package/core/.plugin/plugin.json +1 -1
- package/core/harnesses/claude/hooks/mcp-tool-guard.sh +7 -17
- package/core/harnesses/claude/hooks/pre-bash-guard.sh +7 -17
- package/core/harnesses/claude/hooks/pre-implement-gate.sh +7 -18
- package/core/harnesses/claude/hooks/session-start.sh +5 -3
- package/core/harnesses/claude/hooks/stop-check.sh +5 -3
- package/core/harnesses/claude/hooks/subagent-audit.sh +5 -5
- package/core/harnesses/claude/hooks/user-prompt-submit.sh +5 -3
- package/core/harnesses/codex/hooks/mcp-tool-guard.sh +7 -15
- package/core/harnesses/codex/hooks/pre-bash-guard.sh +7 -15
- package/core/harnesses/codex/hooks/pre-implement-gate.sh +7 -16
- package/core/harnesses/codex/hooks/session-start.sh +5 -3
- package/core/harnesses/codex/hooks/stop-check.sh +6 -5
- package/core/harnesses/codex/hooks/subagent-audit.sh +17 -6
- package/core/harnesses/codex/hooks/user-prompt-submit.sh +5 -4
- package/core/harnesses/gemini/hooks/mcp-tool-guard.sh +7 -18
- package/core/harnesses/gemini/hooks/pre-bash-guard.sh +7 -18
- package/core/harnesses/gemini/hooks/pre-implement-gate.sh +7 -19
- package/core/harnesses/gemini/hooks/session-start.sh +5 -3
- package/core/harnesses/kiro/hooks/mcp-tool-guard.sh +20 -5
- package/core/harnesses/kiro/hooks/pre-bash-guard.sh +20 -5
- package/core/harnesses/kiro/hooks/pre-implement-gate.sh +20 -5
- package/core/harnesses/kiro/hooks/session-start.sh +11 -5
- package/core/harnesses/kiro/hooks/stop-check.sh +21 -30
- package/core/harnesses/pi/extensions/doflow-guard.js +159 -0
- package/core/harnesses/shared/hooks/ENVELOPE_MIGRATION.md +27 -11
- package/core/harnesses/shared/hooks/adapters/antigravity.js +14 -57
- package/core/harnesses/shared/hooks/bridge/bridge.sh +184 -0
- package/core/harnesses/shared/hooks/bridge/envelope.jq +60 -0
- package/core/harnesses/shared/hooks/bridge/respond.sh +62 -0
- package/core/harnesses/shared/hooks/policies/capture-failure.sh +4 -2
- package/core/harnesses/shared/hooks/policies/contract.sh +75 -0
- package/core/harnesses/shared/hooks/policies/deny-json.sh +39 -4
- package/core/harnesses/shared/hooks/policies/lib.sh +4 -2
- package/core/harnesses/shared/hooks/policies/mcp-tool-guard.sh +46 -34
- package/core/harnesses/shared/hooks/policies/pre-bash-guard.sh +75 -25
- package/core/harnesses/shared/hooks/policies/pre-implementation-gate.sh +67 -73
- package/core/harnesses/shared/hooks/policies/session-context.sh +37 -7
- package/core/harnesses/shared/hooks/policies/stop-check.sh +42 -15
- package/core/harnesses/shared/hooks/policies/subagent-audit.sh +35 -5
- package/core/harnesses/shared/hooks/policies/user-prompt-submit.sh +40 -7
- package/core/harnesses/shared/hooks/stream-hook-runner.js +69 -181
- package/core/registry/assets.json +19 -0
- package/core/registry/contracts.json +2 -2
- package/core/registry/harnesses.json +196 -10
- package/core/registry/lifecycle.json +2626 -7
- package/core/registry/storage.json +110 -83
- package/core/registry/verification.json +1 -1
- package/core/shared/scripts/doflow/bash/render-puml.sh +2 -2
- package/core/shared/scripts/doflow/bin/doflow-run +10 -6
- package/core/shared/skills/do-design/SKILL.md +6 -6
- package/core/shared/skills/do-execute-plan/SKILL.md +11 -10
- package/core/shared/skills/do-execute-plan/references/readiness_gate.md +2 -0
- package/core/shared/skills/do-execute-plan/references/scaffold.md +2 -2
- package/core/shared/skills/do-implement/SKILL.md +3 -2
- package/core/shared/skills/do-plan/SKILL.md +7 -8
- package/package.json +4 -2
- package/src/adapters/antigravity/{index.ts → index.js} +141 -105
- package/src/adapters/claude/{index.ts → index.js} +63 -49
- package/src/adapters/claude/{mcp.ts → mcp.js} +16 -16
- package/src/adapters/codex/{agents.ts → agents.js} +24 -24
- package/src/adapters/codex/{config.ts → config.js} +49 -49
- package/src/adapters/codex/{hooks.ts → hooks.js} +62 -27
- package/src/adapters/codex/{index.ts → index.js} +148 -105
- package/src/adapters/codex/{mcp.ts → mcp.js} +38 -38
- package/src/adapters/codex/{native-projection.ts → native-projection.js} +5 -5
- package/src/adapters/copilot/{index.ts → index.js} +42 -42
- package/src/adapters/{copy-tree.ts → copy-tree.js} +74 -74
- package/src/adapters/gemini/{hooks.ts → hooks.js} +78 -37
- package/src/adapters/gemini/{index.ts → index.js} +96 -57
- package/src/adapters/{hook-commands.ts → hook-commands.js} +8 -8
- package/src/adapters/{index.ts → index.js} +13 -13
- package/src/adapters/kiro/{index.ts → index.js} +49 -35
- package/src/adapters/{mcp-entries.ts → mcp-entries.js} +55 -55
- package/src/adapters/opencode/{index.ts → index.js} +52 -52
- package/src/adapters/pi/hooks.js +119 -0
- package/src/adapters/pi/{index.ts → index.js} +64 -39
- package/src/adapters/pi/{json-members.ts → json-members.js} +23 -23
- package/src/adapters/pi/{mcp.ts → mcp.js} +70 -70
- package/src/adapters/{resolve-source.ts → resolve-source.js} +1 -1
- package/src/cli/commands/{install.ts → install.js} +25 -25
- package/src/cli/commands/{list-backups.ts → list-backups.js} +4 -4
- package/src/cli/commands/{reconcile.ts → reconcile.js} +19 -19
- package/src/cli/commands/{remove.ts → remove.js} +10 -10
- package/src/cli/commands/{rollback.ts → rollback.js} +17 -17
- package/src/cli/commands/{self-update.ts → self-update.js} +3 -3
- package/src/cli/commands/{status.ts → status.js} +24 -24
- package/src/cli/commands/{tools.ts → tools.js} +16 -16
- package/src/cli/commands/{update.ts → update.js} +20 -20
- package/src/cli/{index.ts → index.js} +43 -31
- package/src/cli/{runtime-commands.ts → runtime-commands.js} +97 -58
- package/src/cli/{shared.ts → shared.js} +40 -40
- package/src/helper/{bash-helper.ts → bash-helper.js} +7 -7
- package/src/helper/{git.ts → git.js} +2 -2
- package/src/helper/{guardrails.ts → guardrails.js} +10 -10
- package/src/helper/{harness-paths.ts → harness-paths.js} +24 -24
- package/src/helper/{marker-merge.ts → marker-merge.js} +4 -4
- package/src/helper/{prompt.ts → prompt.js} +5 -5
- package/src/helper/{repo-root.ts → repo-root.js} +2 -2
- package/src/helper/{settings-merge.ts → settings-merge.js} +27 -27
- package/src/helper/{settings-scope.ts → settings-scope.js} +3 -3
- package/src/helper/{toml.ts → toml.js} +48 -48
- package/src/install/{backup-set.ts → backup-set.js} +16 -16
- package/src/install/{backup.ts → backup.js} +65 -65
- package/src/install/{context.ts → context.js} +15 -15
- package/src/install/{manifest.ts → manifest.js} +27 -27
- package/src/install/{mcp.ts → mcp.js} +30 -30
- package/src/install/{paths.ts → paths.js} +15 -15
- package/src/install/{targets.ts → targets.js} +10 -10
- package/src/install/{tool-lifecycle.ts → tool-lifecycle.js} +20 -20
- package/src/lifecycle/{bash-availability.ts → bash-availability.js} +1 -1
- package/src/lifecycle/coverage.js +57 -0
- package/src/lifecycle/hook-step.js +61 -0
- package/src/lifecycle/{index.ts → index.js} +160 -146
- package/src/lifecycle/{mcp-index.ts → mcp-index.js} +1 -1
- package/src/lifecycle/{policies.ts → policies.js} +11 -11
- package/src/lifecycle/{view.ts → view.js} +28 -28
- package/src/registry/{index.ts → index.js} +195 -66
- package/src/release/build-package.js +91 -0
- package/src/release/changelog-section.js +70 -0
- package/src/release/{sync-plugin-versions.ts → sync-plugin-versions.js} +4 -4
- package/src/runtime/advice/{cli.ts → cli.js} +31 -31
- package/src/runtime/advice/{client.ts → client.js} +44 -44
- package/src/runtime/advice/{config.ts → config.js} +16 -16
- package/src/runtime/advice/{doctor.ts → doctor.js} +32 -32
- package/src/runtime/advice/{endpoint-guard.ts → endpoint-guard.js} +20 -20
- package/src/runtime/advice/{kinds.ts → kinds.js} +16 -16
- package/src/runtime/advice/providers/{ollama-systemone.ts → ollama-systemone.js} +21 -21
- package/src/runtime/advice/{record.ts → record.js} +19 -19
- package/src/runtime/advice/{registry.ts → registry.js} +16 -16
- package/src/runtime/advice/{strength.ts → strength.js} +7 -7
- package/src/runtime/{brief-tools.ts → brief-tools.js} +50 -50
- package/src/runtime/{c4-project.ts → c4-project.js} +27 -27
- package/src/runtime/candidate.js +505 -0
- package/src/runtime/{capability-router.ts → capability-router.js} +39 -39
- package/src/runtime/{checkouts.ts → checkouts.js} +21 -21
- package/src/runtime/{claims.ts → claims.js} +44 -35
- package/src/runtime/{cli-result.ts → cli-result.js} +15 -8
- package/src/runtime/{cli.ts → cli.js} +59 -292
- package/src/runtime/{command-detect.ts → command-detect.js} +47 -47
- package/src/runtime/context/budget.js +203 -0
- package/src/runtime/context/compose.js +1367 -0
- package/src/runtime/context/delivery-store.js +102 -0
- package/src/runtime/context/mandatory.js +594 -0
- package/src/runtime/context/pointer.js +142 -0
- package/src/runtime/context-pack.js +1277 -0
- package/src/runtime/{decision-register.ts → decision-register.js} +67 -67
- package/src/runtime/evidence-fields.js +39 -0
- package/src/runtime/{evidence-ledger.ts → evidence-ledger.js} +32 -25
- package/src/runtime/failure/{capture.ts → capture.js} +29 -29
- package/src/runtime/failure/{classifier.ts → classifier.js} +4 -4
- package/src/runtime/failure/{cli.ts → cli.js} +30 -30
- package/src/runtime/failure/{home.ts → home.js} +6 -6
- package/src/runtime/failure/{store.ts → store.js} +32 -32
- package/src/runtime/{feature-resolve.ts → feature-resolve.js} +6 -6
- package/src/runtime/{freshness.ts → freshness.js} +59 -22
- package/src/runtime/{health.ts → health.js} +72 -72
- package/src/runtime/{history-compactor.ts → history-compactor.js} +25 -25
- package/src/runtime/{implementation-gate.ts → implementation-gate.js} +30 -30
- package/src/runtime/inventory/{coverage.ts → coverage.js} +10 -10
- package/src/runtime/inventory/{identity.ts → identity.js} +2 -2
- package/src/runtime/inventory/{index.ts → index.js} +36 -36
- package/src/runtime/inventory/{precedence.ts → precedence.js} +2 -2
- package/src/runtime/inventory/{read-scopes.ts → read-scopes.js} +9 -9
- package/src/runtime/inventory/{siblings.ts → siblings.js} +15 -15
- package/src/runtime/knowledge/{chunker.ts → chunker.js} +12 -12
- package/src/runtime/knowledge/{hybrid.ts → hybrid.js} +17 -17
- package/src/runtime/knowledge/{index-store.ts → index-store.js} +23 -23
- package/src/runtime/knowledge/{retrieval.ts → retrieval.js} +27 -27
- package/src/runtime/{leak-scan.ts → leak-scan.js} +12 -12
- package/src/runtime/lifecycle/{cli.ts → cli.js} +32 -32
- package/src/runtime/lifecycle/{event-store.ts → event-store.js} +48 -48
- package/src/runtime/lifecycle/{fold.ts → fold.js} +41 -41
- package/src/runtime/lifecycle/{followup.ts → followup.js} +54 -54
- package/src/runtime/lifecycle/{goal.ts → goal.js} +50 -50
- package/src/runtime/lifecycle/{intent-writer.ts → intent-writer.js} +5 -5
- package/src/runtime/lifecycle/{overview.ts → overview.js} +47 -47
- package/src/runtime/lifecycle/{release.ts → release.js} +29 -29
- package/src/runtime/lifecycle/{report-store.ts → report-store.js} +25 -25
- package/src/runtime/lifecycle/{retention.ts → retention.js} +37 -37
- package/src/runtime/lifecycle/{root.ts → root.js} +9 -9
- package/src/runtime/lifecycle/{status.ts → status.js} +64 -64
- package/src/runtime/lifecycle/{store-upkeep.ts → store-upkeep.js} +46 -46
- package/src/runtime/{locator-resolve.ts → locator-resolve.js} +7 -7
- package/src/runtime/{mask.ts → mask.js} +22 -22
- package/src/runtime/{mcp-consultations.ts → mcp-consultations.js} +54 -54
- package/src/runtime/{mcp-reach.ts → mcp-reach.js} +24 -24
- package/src/runtime/{model-router.ts → model-router.js} +20 -20
- package/src/runtime/{outcome.ts → outcome.js} +27 -27
- package/src/runtime/{reach.ts → reach.js} +21 -21
- package/src/runtime/{readiness-record.ts → readiness-record.js} +12 -12
- package/src/runtime/{readiness.ts → readiness.js} +113 -43
- package/src/runtime/{recovery.ts → recovery.js} +15 -15
- package/src/runtime/{research-request.ts → research-request.js} +34 -34
- package/src/runtime/{retrieval-plan.ts → retrieval-plan.js} +453 -104
- package/src/runtime/retrieval-record.js +223 -0
- package/src/runtime/scaffold/{artifacts.ts → artifacts.js} +22 -19
- package/src/runtime/scaffold/{fingerprint.ts → fingerprint.js} +3 -3
- package/src/runtime/scaffold/{generate.ts → generate.js} +109 -109
- package/src/runtime/scaffold/{languages.ts → languages.js} +34 -34
- package/src/runtime/source-version.js +289 -0
- package/src/runtime/storage/{cli.ts → cli.js} +9 -9
- package/src/runtime/storage/{engine.ts → engine.js} +37 -37
- package/src/runtime/storage/{registry.ts → registry.js} +19 -19
- package/src/runtime/storage/{roots.ts → roots.js} +8 -8
- package/src/runtime/storage/{rules.ts → rules.js} +26 -26
- package/src/runtime/storage/{upkeep.ts → upkeep.js} +35 -35
- package/src/runtime/storage/{work-status.ts → work-status.js} +39 -39
- package/src/runtime/{task-attempts.ts → task-attempts.js} +62 -62
- package/src/runtime/{task-classifier.ts → task-classifier.js} +34 -34
- package/src/runtime/{task-scope.ts → task-scope.js} +40 -15
- package/src/runtime/{task-state.ts → task-state.js} +86 -21
- package/src/runtime/trace/{indicators.ts → indicators.js} +22 -22
- package/src/runtime/trace/{ledger.ts → ledger.js} +42 -42
- package/src/runtime/trace/{render.ts → render.js} +7 -7
- package/src/runtime/trace/{views.ts → views.js} +40 -40
- package/src/runtime/verification/{contract-runner.ts → contract-runner.js} +23 -23
- package/src/runtime/verification/{engine.ts → engine.js} +143 -143
- package/src/runtime/verification/{registry.ts → registry.js} +6 -6
- package/src/runtime/verification/{scope-bound.ts → scope-bound.js} +50 -50
- package/src/runtime/verification/{test-selection.ts → test-selection.js} +28 -28
- package/src/runtime/{workflow-engine.ts → workflow-engine.js} +50 -50
- package/src/runtime/{workflow-orchestrator.ts → workflow-orchestrator.js} +91 -91
- package/src/runtime/{worktree.ts → worktree.js} +31 -31
- package/src/state/{index.ts → index.js} +26 -26
- package/src/state/{lockfile.ts → lockfile.js} +20 -20
- package/src/state/{run-lock.ts → run-lock.js} +44 -44
- package/core/.antigravity-plugin/hooks.json +0 -39
- package/core/harnesses/shared/hooks/adapters/arg-fields.js +0 -19
- package/core/harnesses/shared/hooks/adapters/gemini.js +0 -114
- package/core/harnesses/shared/hooks/hooks.json +0 -47
- package/src/runtime/context-pack.ts +0 -492
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Native hook response renderers for the shared lifecycle bridge.
|
|
3
|
+
case ${BASH_SOURCE[0]} in */*) _DOFLOW_RESPOND_DIR=${BASH_SOURCE[0]%/*} ;; *) _DOFLOW_RESPOND_DIR=. ;; esac
|
|
4
|
+
. "$_DOFLOW_RESPOND_DIR/../policies/deny-json.sh"
|
|
5
|
+
|
|
6
|
+
doflow_bridge_native_event_var() {
|
|
7
|
+
DF_NE=$2
|
|
8
|
+
case "$1:$2" in
|
|
9
|
+
kiro:preToolUse) DF_NE=PreToolUse ;;
|
|
10
|
+
kiro:agentSpawn) DF_NE=SessionStart ;;
|
|
11
|
+
kiro:stop) DF_NE=Stop ;;
|
|
12
|
+
esac
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
doflow_bridge_native_event() {
|
|
16
|
+
local harness="$1" event="$2"
|
|
17
|
+
case "$harness:$event" in
|
|
18
|
+
kiro:preToolUse) event=PreToolUse ;;
|
|
19
|
+
kiro:agentSpawn) event=SessionStart ;;
|
|
20
|
+
kiro:stop) event=Stop ;;
|
|
21
|
+
esac
|
|
22
|
+
printf '%s\n' "$event"
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
doflow_render_response() {
|
|
26
|
+
local harness="$1" event="$2" outcome="$3" reason="$4" status="$5" engine="${DOFLOW_KIRO_ENGINE:-2x}"
|
|
27
|
+
doflow_bridge_native_event_var "$harness" "$event"; event=$DF_NE
|
|
28
|
+
if [ "$outcome" = deny ]; then
|
|
29
|
+
case "$harness:$event" in
|
|
30
|
+
claude:PreToolUse|codex:PreToolUse) emit_pretooluse_deny_nested "$reason" || return $?; return 0 ;;
|
|
31
|
+
gemini:BeforeTool) emit_pretooluse_deny_flat "$reason"; return 0 ;;
|
|
32
|
+
kiro:PreToolUse) printf '%s' "$reason" >&2; return 2 ;;
|
|
33
|
+
antigravity:PreToolUse)
|
|
34
|
+
if command -v jq >/dev/null 2>&1; then jq -cn --arg reason "$reason" '{decision:"deny",reason:$reason}'
|
|
35
|
+
else printf '{"decision":"deny","reason":%s}\n' "$( _doflow_json_reason "$reason" )"; fi
|
|
36
|
+
return $? ;;
|
|
37
|
+
antigravity:Stop)
|
|
38
|
+
if command -v jq >/dev/null 2>&1; then jq -cn --arg reason "$reason" '{decision:"continue",reason:$reason}'
|
|
39
|
+
else printf '{"decision":"continue","reason":%s}\n' "$( _doflow_json_reason "$reason" )"; fi
|
|
40
|
+
return $? ;;
|
|
41
|
+
pi:tool_call)
|
|
42
|
+
if command -v jq >/dev/null 2>&1; then jq -cn --arg reason "$reason" '{block:true,reason:$reason}'
|
|
43
|
+
else printf '{"block":true,"reason":%s}\n' "$( _doflow_json_reason "$reason" )"; fi
|
|
44
|
+
return $? ;;
|
|
45
|
+
kiro:Stop|kiro:stop)
|
|
46
|
+
if [ "$engine" = v3 ]; then
|
|
47
|
+
if command -v jq >/dev/null 2>&1; then jq -cn --arg reason "$reason" '{decision:"block",reason:$reason}'
|
|
48
|
+
else printf '{"decision":"block","reason":%s}\n' "$( _doflow_json_reason "$reason" )"; fi
|
|
49
|
+
return $?
|
|
50
|
+
fi
|
|
51
|
+
printf '%s' "$reason" >&2; return 2 ;;
|
|
52
|
+
codex:Stop|codex:stop) printf '%s\n' '{}' >&1; printf '%s\n' "$reason" >&2; return 2 ;;
|
|
53
|
+
*) printf '%s\n' "$reason" >&2; return 2 ;;
|
|
54
|
+
esac
|
|
55
|
+
fi
|
|
56
|
+
case "$harness:$event" in
|
|
57
|
+
codex:Stop|codex:stop) printf '%s\n' '{}' ;;
|
|
58
|
+
antigravity:PreToolUse) printf '%s\n' '{"decision":"allow"}' ;;
|
|
59
|
+
kiro:PreToolUse|claude:Stop) if [ "$status" -ne 0 ]; then printf '%s' "$reason" >&2; fi ;;
|
|
60
|
+
esac
|
|
61
|
+
return "$status"
|
|
62
|
+
}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
# example its pattern file is missing). It exists only to be sourced, in a subshell with all output
|
|
7
7
|
# discarded, from inside a branch where the policy already fails open:
|
|
8
8
|
#
|
|
9
|
-
# ( . "
|
|
9
|
+
# ( . "<policy-dir>/capture-failure.sh"; doflow_capture_failure pre-bash-guard patterns-missing ) >/dev/null 2>&1 || true
|
|
10
10
|
#
|
|
11
11
|
# The subshell keeps anything this file does away from the policy, which runs with `set -uo pipefail`,
|
|
12
12
|
# does not source lib.sh and would change exit codes under `set -e`. So this file:
|
|
@@ -15,6 +15,8 @@
|
|
|
15
15
|
# - reads no stdin and writes nothing to stdout or stderr;
|
|
16
16
|
# - never rotates the file (rotation is done by the Node writers only, DEC-019);
|
|
17
17
|
# - never blocks, never changes the policy's decision, and swallows every error.
|
|
18
|
+
|
|
19
|
+
case ${BASH_SOURCE[0]} in */*) _DOFLOW_CAPTURE_FAILURE_DIR=${BASH_SOURCE[0]%/*} ;; *) _DOFLOW_CAPTURE_FAILURE_DIR=. ;; esac
|
|
18
20
|
# The line holds only the policy name and the point name from DoFlow's own code, the working
|
|
19
21
|
# directory, the harness name and the install version: never the tool call, its arguments or stdin.
|
|
20
22
|
# It is a no-op when no failure home can be resolved (HOME unset, or XDG_CONFIG_HOME relative) and
|
|
@@ -54,7 +56,7 @@ doflow_capture_failure() {
|
|
|
54
56
|
[[ "$harness" =~ ^[A-Za-z0-9._-]{1,40}$ ]] || harness="none"
|
|
55
57
|
|
|
56
58
|
# script_version of the nearest install manifest: walking up from this file, then the global one.
|
|
57
|
-
here="$(cd
|
|
59
|
+
here="$(CDPATH= cd -P -- "$_DOFLOW_CAPTURE_FAILURE_DIR" 2>/dev/null && pwd -P)" || here=""
|
|
58
60
|
dir="$here"
|
|
59
61
|
while [ -n "$dir" ] && [ -z "$version" ]; do
|
|
60
62
|
if [ -f "$dir/.doflow/.install-manifest.json" ]; then
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# contract.sh — shared decision and envelope contract for canonical policies.
|
|
3
|
+
#
|
|
4
|
+
# This file is sourced by policy scripts. It sets no shell options, installs no traps, reads no
|
|
5
|
+
# stdin, and keeps decision recording independent of jq.
|
|
6
|
+
|
|
7
|
+
doflow_decision_init() {
|
|
8
|
+
DF_POLICY="${1:-}"
|
|
9
|
+
DF_POLICY_VERSION="${2:-0}"
|
|
10
|
+
doflow_decide allow allow
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
doflow_decide() {
|
|
14
|
+
local LC_ALL=C
|
|
15
|
+
local outcome="${1:-allow}" code="${2:-invalid-code}" fd="${DOFLOW_DECISION_FD:-}"
|
|
16
|
+
case "$code" in
|
|
17
|
+
[a-z]*)
|
|
18
|
+
case "$code" in
|
|
19
|
+
*[!a-z0-9:._-]*) code=invalid-code ;;
|
|
20
|
+
*) [ "${#code}" -le 40 ] || code=invalid-code ;;
|
|
21
|
+
esac
|
|
22
|
+
;;
|
|
23
|
+
*) code=invalid-code ;;
|
|
24
|
+
esac
|
|
25
|
+
case "$fd" in
|
|
26
|
+
3|4|5|6|7|8|9)
|
|
27
|
+
# A caller may set the descriptor name without passing an open fd. Keep
|
|
28
|
+
# that broken handoff silent and harmless, including under `set -e`.
|
|
29
|
+
{ printf '\036{"v":1,"policy":"%s","policyVersion":%s,"outcome":"%s","code":"%s"}\n' \
|
|
30
|
+
"${DF_POLICY:-}" "${DF_POLICY_VERSION:-0}" "$outcome" "$code" >&"$fd"; } 2>/dev/null || :
|
|
31
|
+
;;
|
|
32
|
+
esac
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
doflow_read_envelope() {
|
|
36
|
+
local assignments
|
|
37
|
+
if [ -z "${INPUT:-}" ]; then
|
|
38
|
+
DF_ENV_CLASS=missing
|
|
39
|
+
DF_ENV_CODE=envelope-missing
|
|
40
|
+
DF_ENV_HARNESS=
|
|
41
|
+
DF_ENV_EVENT=
|
|
42
|
+
DF_ENV_OP=
|
|
43
|
+
DF_ENV_TOOL=
|
|
44
|
+
DF_ENV_PATHS=
|
|
45
|
+
DF_ENV_COMMAND=
|
|
46
|
+
DF_ENV_PATCH=
|
|
47
|
+
DF_ENV_ROOT=
|
|
48
|
+
DF_ENV_TASK=
|
|
49
|
+
return 0
|
|
50
|
+
fi
|
|
51
|
+
assignments=$(printf '%s' "${INPUT:-}" | jq -sr '
|
|
52
|
+
(.[0]) as $root |
|
|
53
|
+
($root | if type == "object" then . else {} end) as $object |
|
|
54
|
+
($object.doflow_event // {}) as $event |
|
|
55
|
+
($event | if type == "object" then . else {} end) as $fields |
|
|
56
|
+
def field($key): ($fields[$key] // "") | if type == "string" then . else "" end;
|
|
57
|
+
def shellvar($name; $value): ($name + "=" + ($value | @sh));
|
|
58
|
+
(if ($root | type) != "object" or (($event | type) != "object") then "missing"
|
|
59
|
+
elif ($event | has("version") | not) then "missing"
|
|
60
|
+
elif ($event.version | type) == "number" and $event.version == 1 then "v1"
|
|
61
|
+
else "unsupported" end) as $class |
|
|
62
|
+
[shellvar("DF_ENV_CLASS"; $class),
|
|
63
|
+
shellvar("DF_ENV_CODE"; (if $class == "missing" then "envelope-missing" else "" end)),
|
|
64
|
+
shellvar("DF_ENV_HARNESS"; field("harness")),
|
|
65
|
+
shellvar("DF_ENV_EVENT"; field("event")),
|
|
66
|
+
shellvar("DF_ENV_OP"; field("operation")),
|
|
67
|
+
shellvar("DF_ENV_TOOL"; field("tool")),
|
|
68
|
+
shellvar("DF_ENV_PATHS"; ([$fields.paths[]? | select(type == "string")] | join("\n"))),
|
|
69
|
+
shellvar("DF_ENV_COMMAND"; field("command")),
|
|
70
|
+
shellvar("DF_ENV_PATCH"; field("patch")),
|
|
71
|
+
shellvar("DF_ENV_ROOT"; field("projectRoot")),
|
|
72
|
+
shellvar("DF_ENV_TASK"; field("taskId"))][]
|
|
73
|
+
' 2>/dev/null) || return 1
|
|
74
|
+
eval "$assignments"
|
|
75
|
+
}
|
|
@@ -8,22 +8,57 @@
|
|
|
8
8
|
# the front door right there, before it ever reached the code below that emits
|
|
9
9
|
# the deny JSON. Sourcing this file must never change the caller's shell options.
|
|
10
10
|
#
|
|
11
|
-
# Usage: source "
|
|
11
|
+
# Usage: source "<policy-dir>/deny-json.sh"
|
|
12
|
+
|
|
13
|
+
case ${BASH_SOURCE[0]} in */*) _DOFLOW_DENY_JSON_DIR=${BASH_SOURCE[0]%/*} ;; *) _DOFLOW_DENY_JSON_DIR=. ;; esac
|
|
14
|
+
|
|
15
|
+
# Escape a string as a JSON string body without external tools. Work bytewise so
|
|
16
|
+
# every control byte is handled the same way regardless of the caller's locale.
|
|
17
|
+
doflow_json_escape() {
|
|
18
|
+
local LC_ALL=C input="${1-}" output="" char ordinal i
|
|
19
|
+
for ((i = 0; i < ${#input}; i++)); do
|
|
20
|
+
char="${input:i:1}"
|
|
21
|
+
case "$char" in
|
|
22
|
+
'\') output="$output\\\\" ;;
|
|
23
|
+
'"') output="$output\\\"" ;;
|
|
24
|
+
$'\n') output="$output\\n" ;;
|
|
25
|
+
$'\r') output="$output\\r" ;;
|
|
26
|
+
$'\t') output="$output\\t" ;;
|
|
27
|
+
*)
|
|
28
|
+
printf -v ordinal '%d' "'${char}"
|
|
29
|
+
if [ "$ordinal" -ge 32 ]; then output="$output$char"; fi
|
|
30
|
+
;;
|
|
31
|
+
esac
|
|
32
|
+
done
|
|
33
|
+
printf '%s' "$output"
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
_doflow_json_reason() {
|
|
37
|
+
printf '"%s"' "$(doflow_json_escape "$1")"
|
|
38
|
+
}
|
|
12
39
|
|
|
13
40
|
# Claude/Codex shape: nested under hookSpecificOutput.permissionDecision.
|
|
14
41
|
emit_pretooluse_deny_nested() {
|
|
15
42
|
local reason="$1"
|
|
16
|
-
|
|
43
|
+
if command -v jq >/dev/null 2>&1; then
|
|
44
|
+
jq -n --arg reason "$reason" '{
|
|
17
45
|
hookSpecificOutput: {
|
|
18
46
|
hookEventName: "PreToolUse",
|
|
19
47
|
permissionDecision: "deny",
|
|
20
48
|
permissionDecisionReason: $reason
|
|
21
49
|
}
|
|
22
|
-
|
|
50
|
+
}'
|
|
51
|
+
else
|
|
52
|
+
printf '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"deny","permissionDecisionReason":%s}}\n' "$(_doflow_json_reason "$reason")"
|
|
53
|
+
fi
|
|
23
54
|
}
|
|
24
55
|
|
|
25
56
|
# Gemini shape: flat {decision, reason} (packages/core/src/hooks/types.ts).
|
|
26
57
|
emit_pretooluse_deny_flat() {
|
|
27
58
|
local reason="$1"
|
|
28
|
-
|
|
59
|
+
if command -v jq >/dev/null 2>&1; then
|
|
60
|
+
jq -n --arg reason "$reason" '{decision: "deny", reason: $reason}'
|
|
61
|
+
else
|
|
62
|
+
printf '{"decision":"deny","reason":%s}\n' "$(_doflow_json_reason "$reason")"
|
|
63
|
+
fi
|
|
29
64
|
}
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
# lib.sh — shared constants and helpers for the Canonical Policy Library
|
|
3
3
|
#
|
|
4
|
-
# Usage: source "
|
|
4
|
+
# Usage: source "<policy-dir>/lib.sh"
|
|
5
5
|
#
|
|
6
6
|
# All hooks must:
|
|
7
7
|
# 1. source this file at the top
|
|
8
8
|
# 2. call require_jq immediately after
|
|
9
9
|
# 3. capture stdin once: INPUT=$(cat)
|
|
10
10
|
# 4. use: json_field "$INPUT" ".field_name"
|
|
11
|
+
|
|
12
|
+
case ${BASH_SOURCE[0]} in */*) _DOFLOW_LIB_DIR=${BASH_SOURCE[0]%/*} ;; *) _DOFLOW_LIB_DIR=. ;; esac
|
|
11
13
|
#
|
|
12
14
|
# Deployment:
|
|
13
15
|
# This file is installed by the doflow CLI and overwritten on update — do
|
|
@@ -327,7 +329,7 @@ storage_upkeep_async() {
|
|
|
327
329
|
# Background the fully redirected subshell; an inner `&` leaves this shell
|
|
328
330
|
# holding the hook's captured stdout open until the dispatcher exits. The
|
|
329
331
|
# storage verb takes its project from the working directory: run it at the root.
|
|
330
|
-
( cd "$root" 2>/dev/null && nohup "$run" storage --action upkeep </dev/null >/dev/null 2>&1 ) >/dev/null 2>&1 &
|
|
332
|
+
( cd "$root" 2>/dev/null && nohup "$run" storage --action upkeep </dev/null >/dev/null 2>&1 ) >/dev/null 2>&1 3>&- 5>&- &
|
|
331
333
|
return 0
|
|
332
334
|
done
|
|
333
335
|
return 0
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
|
+
case $0 in */*) _DOFLOW_POLDIR=${0%/*} ;; *) _DOFLOW_POLDIR=. ;; esac
|
|
2
3
|
# mcp-tool-guard.sh — Canonical Policy Library: PreToolUse(mcp_*) hook
|
|
3
4
|
#
|
|
4
5
|
# Intercepts every MCP tool call and blocks matches against mcp-policy.conf, the
|
|
@@ -13,19 +14,9 @@
|
|
|
13
14
|
# Canonical Policy Script Contract (design.md §4):
|
|
14
15
|
# env DOFLOW_PROJECT_DIR, DOFLOW_AGENT (both optional; not required to
|
|
15
16
|
# evaluate this policy)
|
|
16
|
-
# stdin a
|
|
17
|
-
#
|
|
18
|
-
#
|
|
19
|
-
# with no payload translation of its own.
|
|
20
|
-
# exit 0 = allow, non-zero = deny with the reason on stderr.
|
|
21
|
-
#
|
|
22
|
-
# Reconciliation note (022-normalize-hooks, Phase A): MCP tool-name
|
|
23
|
-
# vocabularies differ per harness — Claude/Codex/Kiro use a double-underscore
|
|
24
|
-
# "mcp__<server>__<tool>" convention, Gemini documents a single-underscore
|
|
25
|
-
# "mcp_<server>_<tool>" convention. A single-underscore prefix check ("^mcp_")
|
|
26
|
-
# matches both conventions (the double-underscore form is a superset match of
|
|
27
|
-
# the single-underscore prefix), so this script uses that one generic check
|
|
28
|
-
# instead of hardcoding either harness's exact convention.
|
|
17
|
+
# stdin a native payload with envelope v1 under doflow_event; only the
|
|
18
|
+
# envelope's operation and tool fields are used.
|
|
19
|
+
# exit exit 2 + doflow_decide deny = deny; other faults per IC-011.
|
|
29
20
|
#
|
|
30
21
|
# mcp-policy.conf's shape (TAB-separated regex<TAB>reason, '#'-comments,
|
|
31
22
|
# blank lines skipped) is identical across every harness that ships one today
|
|
@@ -34,36 +25,56 @@
|
|
|
34
25
|
|
|
35
26
|
set -uo pipefail
|
|
36
27
|
|
|
37
|
-
command -v jq >/dev/null 2>&1 || exit 0 # no jq -> cannot evaluate -> allow
|
|
38
|
-
|
|
39
28
|
INPUT=$(cat)
|
|
29
|
+
if [ -f "$_DOFLOW_POLDIR/contract.sh" ]; then
|
|
30
|
+
. "$_DOFLOW_POLDIR/contract.sh"
|
|
31
|
+
else
|
|
32
|
+
doflow_decision_init() { :; }
|
|
33
|
+
doflow_decide() { :; }
|
|
34
|
+
doflow_read_envelope() { DF_ENV_CLASS=missing; DF_ENV_CODE=contract-missing; }
|
|
35
|
+
( . "$_DOFLOW_POLDIR/capture-failure.sh"; doflow_capture_failure mcp-tool-guard contract-missing ) >/dev/null 2>&1 || true
|
|
36
|
+
if [[ "${DOFLOW_AGENT:-}" =~ ^[a-z]+$ ]]; then
|
|
37
|
+
printf '[mcp-tool-guard] DoFlow hooks out of step; run: doflow update -t %s\n' "$DOFLOW_AGENT" >&2
|
|
38
|
+
else
|
|
39
|
+
printf '[mcp-tool-guard] DoFlow hooks out of step; run: doflow update\n' >&2
|
|
40
|
+
fi
|
|
41
|
+
doflow_decide deny contract-missing
|
|
42
|
+
exit 2
|
|
43
|
+
fi
|
|
44
|
+
doflow_decision_init mcp-tool-guard 1
|
|
45
|
+
command -v jq >/dev/null 2>&1 || {
|
|
46
|
+
doflow_decide error dependency-missing:jq
|
|
47
|
+
( . "$_DOFLOW_POLDIR/capture-failure.sh"; doflow_capture_failure mcp-tool-guard dependency-missing:jq ) >/dev/null 2>&1 || true
|
|
48
|
+
exit 0
|
|
49
|
+
}
|
|
50
|
+
DF_ENV_CLASS=missing DF_ENV_CODE=envelope-missing
|
|
51
|
+
doflow_read_envelope || true
|
|
40
52
|
|
|
41
53
|
TOOL_NAME=""
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
esac
|
|
58
|
-
shopt -u nocasematch 2>/dev/null || true
|
|
54
|
+
if [ "$DF_ENV_CLASS" = v1 ]; then
|
|
55
|
+
[ "$DF_ENV_OP" = mcp ] || exit 0
|
|
56
|
+
TOOL_NAME=$DF_ENV_TOOL
|
|
57
|
+
elif [ "$DF_ENV_CLASS" = unsupported ]; then
|
|
58
|
+
doflow_decide unsupported envelope-version
|
|
59
|
+
( . "$_DOFLOW_POLDIR/capture-failure.sh"; doflow_capture_failure mcp-tool-guard unsupported:envelope-version ) >/dev/null 2>&1 || true
|
|
60
|
+
exit 0
|
|
61
|
+
else
|
|
62
|
+
if [ "${DF_ENV_CODE:-envelope-missing}" != contract-missing ]; then
|
|
63
|
+
( . "$_DOFLOW_POLDIR/capture-failure.sh"; doflow_capture_failure mcp-tool-guard envelope-missing ) >/dev/null 2>&1 || true
|
|
64
|
+
fi
|
|
65
|
+
if [[ "${DOFLOW_AGENT:-}" =~ ^[a-z]+$ ]]; then printf '[mcp-tool-guard] DoFlow hooks out of step; run: doflow update -t %s\n' "$DOFLOW_AGENT" >&2; else printf '[mcp-tool-guard] DoFlow hooks out of step; run: doflow update\n' >&2; fi
|
|
66
|
+
doflow_decide deny "${DF_ENV_CODE:-envelope-missing}"
|
|
67
|
+
exit 2
|
|
68
|
+
fi
|
|
59
69
|
|
|
60
|
-
POLICY_FILE="$
|
|
70
|
+
POLICY_FILE="$_DOFLOW_POLDIR/mcp-policy.conf"
|
|
61
71
|
|
|
62
72
|
# If policy file is missing, allow everything (fail open — don't block the agent).
|
|
63
73
|
if [ ! -f "$POLICY_FILE" ]; then
|
|
64
74
|
# The policy file ships with DoFlow, so a missing one means a broken install: record it on this
|
|
65
75
|
# machine (feature 046, IC-018) and fail open exactly as before. Subshell, output discarded.
|
|
66
|
-
( . "$
|
|
76
|
+
( . "$_DOFLOW_POLDIR/capture-failure.sh"; doflow_capture_failure mcp-tool-guard policy-file-missing ) >/dev/null 2>&1 || true
|
|
77
|
+
doflow_decide error mcp-policy-missing
|
|
67
78
|
exit 0
|
|
68
79
|
fi
|
|
69
80
|
|
|
@@ -83,6 +94,7 @@ while IFS=$'\t' read -r pattern reason || [ -n "$pattern" ]; do
|
|
|
83
94
|
|
|
84
95
|
if [ "$matched" = "true" ]; then
|
|
85
96
|
echo "[mcp-tool-guard] ${reason:-MCP tool call blocked by mcp-tool-guard} (tool: $TOOL_NAME)" >&2
|
|
97
|
+
doflow_decide deny blocked-pattern
|
|
86
98
|
exit 2
|
|
87
99
|
fi
|
|
88
100
|
done < "$POLICY_FILE"
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
|
+
case $0 in */*) _DOFLOW_POLDIR=${0%/*} ;; *) _DOFLOW_POLDIR=. ;; esac
|
|
2
3
|
# pre-bash-guard.sh — Canonical Policy Library: PreToolUse(shell-command) hook
|
|
3
4
|
#
|
|
4
5
|
# Intercepts every shell-command tool call and blocks dangerous commands by
|
|
@@ -10,14 +11,9 @@
|
|
|
10
11
|
# Canonical Policy Script Contract (design.md §4):
|
|
11
12
|
# env DOFLOW_PROJECT_DIR, DOFLOW_AGENT (both optional; not required to
|
|
12
13
|
# evaluate this policy)
|
|
13
|
-
# stdin a
|
|
14
|
-
#
|
|
15
|
-
#
|
|
16
|
-
# run_shell_command — Gemini; run_command — the generic name
|
|
17
|
-
# stream-hook-runner.js's own COMMAND_TOOL_PATTERN already
|
|
18
|
-
# recognized), since a native front door (Claude/Codex/Kiro) execs
|
|
19
|
-
# this script directly with no payload translation of its own.
|
|
20
|
-
# exit 0 = allow, non-zero = deny with the reason on stderr.
|
|
14
|
+
# stdin a native payload with envelope v1 under doflow_event; only the
|
|
15
|
+
# envelope's operation, tool and command fields are used.
|
|
16
|
+
# exit exit 2 + doflow_decide deny = deny; other faults per IC-011.
|
|
21
17
|
#
|
|
22
18
|
# Reconciliation note (022-normalize-hooks, Phase A / D1): this also absorbs
|
|
23
19
|
# stream-hook-runner.js's inline DESTRUCTIVE_COMMAND_PATTERN
|
|
@@ -38,29 +34,54 @@
|
|
|
38
34
|
|
|
39
35
|
set -uo pipefail
|
|
40
36
|
|
|
41
|
-
command -v jq >/dev/null 2>&1 || exit 0 # no jq -> cannot evaluate -> allow
|
|
42
|
-
|
|
43
37
|
INPUT=$(cat)
|
|
38
|
+
if [ -f "$_DOFLOW_POLDIR/contract.sh" ]; then
|
|
39
|
+
. "$_DOFLOW_POLDIR/contract.sh"
|
|
40
|
+
else
|
|
41
|
+
doflow_decision_init() { :; }
|
|
42
|
+
doflow_decide() { :; }
|
|
43
|
+
doflow_read_envelope() { DF_ENV_CLASS=missing; DF_ENV_CODE=contract-missing; }
|
|
44
|
+
( . "$_DOFLOW_POLDIR/capture-failure.sh"; doflow_capture_failure pre-bash-guard contract-missing ) >/dev/null 2>&1 || true
|
|
45
|
+
if [[ "${DOFLOW_AGENT:-}" =~ ^[a-z]+$ ]]; then
|
|
46
|
+
printf '[pre-bash-guard] DoFlow hooks out of step; run: doflow update -t %s\n' "$DOFLOW_AGENT" >&2
|
|
47
|
+
else
|
|
48
|
+
printf '[pre-bash-guard] DoFlow hooks out of step; run: doflow update\n' >&2
|
|
49
|
+
fi
|
|
50
|
+
doflow_decide deny contract-missing
|
|
51
|
+
exit 2
|
|
52
|
+
fi
|
|
53
|
+
doflow_decision_init pre-bash-guard 1
|
|
54
|
+
command -v jq >/dev/null 2>&1 || {
|
|
55
|
+
doflow_decide error dependency-missing:jq
|
|
56
|
+
( . "$_DOFLOW_POLDIR/capture-failure.sh"; doflow_capture_failure pre-bash-guard dependency-missing:jq ) >/dev/null 2>&1 || true
|
|
57
|
+
exit 0
|
|
58
|
+
}
|
|
59
|
+
DF_ENV_CLASS=missing DF_ENV_CODE=envelope-missing
|
|
60
|
+
doflow_read_envelope || true
|
|
44
61
|
|
|
45
62
|
TOOL_NAME=""
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
[
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
63
|
+
COMMAND=""
|
|
64
|
+
if [ "$DF_ENV_CLASS" = v1 ]; then
|
|
65
|
+
[ "$DF_ENV_OP" = command ] || exit 0
|
|
66
|
+
TOOL_NAME=$DF_ENV_TOOL
|
|
67
|
+
COMMAND=$DF_ENV_COMMAND
|
|
68
|
+
elif [ "$DF_ENV_CLASS" = unsupported ]; then
|
|
69
|
+
doflow_decide unsupported envelope-version
|
|
70
|
+
( . "$_DOFLOW_POLDIR/capture-failure.sh"; doflow_capture_failure pre-bash-guard unsupported:envelope-version ) >/dev/null 2>&1 || true
|
|
71
|
+
exit 0
|
|
72
|
+
else
|
|
73
|
+
if [ "${DF_ENV_CODE:-envelope-missing}" != contract-missing ]; then
|
|
74
|
+
( . "$_DOFLOW_POLDIR/capture-failure.sh"; doflow_capture_failure pre-bash-guard envelope-missing ) >/dev/null 2>&1 || true
|
|
75
|
+
fi
|
|
76
|
+
if [[ "${DOFLOW_AGENT:-}" =~ ^[a-z]+$ ]]; then printf '[pre-bash-guard] DoFlow hooks out of step; run: doflow update -t %s\n' "$DOFLOW_AGENT" >&2; else printf '[pre-bash-guard] DoFlow hooks out of step; run: doflow update\n' >&2; fi
|
|
77
|
+
doflow_decide deny "${DF_ENV_CODE:-envelope-missing}"
|
|
78
|
+
exit 2
|
|
79
|
+
fi
|
|
59
80
|
|
|
60
81
|
# Nothing to check if command is empty
|
|
61
82
|
[ -z "$COMMAND" ] && exit 0
|
|
62
83
|
|
|
63
|
-
PATTERNS_FILE="$
|
|
84
|
+
PATTERNS_FILE="$_DOFLOW_POLDIR/blocked-patterns.conf"
|
|
64
85
|
|
|
65
86
|
# ASCII-only patterns: the C locale keeps bash's substring/pattern operations
|
|
66
87
|
# byte-wise (much faster than multibyte-aware in a UTF-8 locale).
|
|
@@ -268,17 +289,21 @@ if [ ! -f "$PATTERNS_FILE" ]; then
|
|
|
268
289
|
# The pattern file ships with DoFlow, so a missing one means a broken install: record it on this
|
|
269
290
|
# machine (feature 046, IC-018) and go on exactly as before. Subshell, output discarded: nothing
|
|
270
291
|
# the helper does can reach this policy's options, variables, output or exit status.
|
|
271
|
-
( . "$
|
|
292
|
+
( . "$_DOFLOW_POLDIR/capture-failure.sh"; doflow_capture_failure pre-bash-guard patterns-missing ) >/dev/null 2>&1 || true
|
|
293
|
+
doflow_decide error patterns-missing
|
|
272
294
|
if _floor_rm_hits '/+\*{0,2}'; then
|
|
273
295
|
echo "[pre-bash-guard] Catastrophic delete blocked — recursive rm of the root directory (/)" >&2
|
|
296
|
+
doflow_decide deny catastrophic-rm-root
|
|
274
297
|
exit 2
|
|
275
298
|
fi
|
|
276
299
|
if _floor_rm_hits '(~[A-Za-z_][A-Za-z0-9_.-]*|~|\$HOME|\$\{HOME\})(/\*{0,2})?'; then
|
|
277
300
|
echo "[pre-bash-guard] Catastrophic delete blocked — recursive rm of the home directory" >&2
|
|
301
|
+
doflow_decide deny catastrophic-rm-home
|
|
278
302
|
exit 2
|
|
279
303
|
fi
|
|
280
304
|
if _floor_rm_hits '/(Users|home|etc|usr|bin|sbin|var|opt|System|Library|Applications|Volumes|private|root|boot|lib|dev|proc)(/\*{0,2})?'; then
|
|
281
305
|
echo "[pre-bash-guard] Catastrophic delete blocked — recursive rm of a system directory" >&2
|
|
306
|
+
doflow_decide deny catastrophic-rm-system
|
|
282
307
|
exit 2
|
|
283
308
|
fi
|
|
284
309
|
# No patterns file beyond the hardcoded floor — allow everything else
|
|
@@ -286,6 +311,30 @@ if [ ! -f "$PATTERNS_FILE" ]; then
|
|
|
286
311
|
exit 0
|
|
287
312
|
fi
|
|
288
313
|
|
|
314
|
+
# One combined ERE cheaply rules out commands that match no live pattern. A
|
|
315
|
+
# grep match or regex error falls through to the original per-line loop, which
|
|
316
|
+
# still owns the reason and optional exclude semantics.
|
|
317
|
+
_anchored_patterns="" _full_patterns="" _has_backreference=false
|
|
318
|
+
while IFS=$'\t' read -r pattern _reason _exclude || [ -n "$pattern" ]; do
|
|
319
|
+
[ -z "$pattern" ] && continue
|
|
320
|
+
case "$pattern" in \#*) continue ;; esac
|
|
321
|
+
if [[ "$pattern" =~ \\[1-9] ]]; then _has_backreference=true; fi
|
|
322
|
+
case "$pattern" in
|
|
323
|
+
'(^|'*) _anchored_patterns="${_anchored_patterns:+$_anchored_patterns|}($pattern)" ;;
|
|
324
|
+
*) _full_patterns="${_full_patterns:+$_full_patterns|}($pattern)" ;;
|
|
325
|
+
esac
|
|
326
|
+
done < "$PATTERNS_FILE"
|
|
327
|
+
_anchored_status=1 _full_status=1
|
|
328
|
+
if [ -n "$_anchored_patterns" ]; then
|
|
329
|
+
grep -qiE -- "$_anchored_patterns" <<<"$SHELL_TEXT" 2>/dev/null
|
|
330
|
+
_anchored_status=$?
|
|
331
|
+
fi
|
|
332
|
+
if [ -n "$_full_patterns" ]; then
|
|
333
|
+
grep -qiE -- "$_full_patterns" <<<"$COMMAND" 2>/dev/null
|
|
334
|
+
_full_status=$?
|
|
335
|
+
fi
|
|
336
|
+
if [ "$_anchored_status" -eq 1 ] && [ "$_full_status" -eq 1 ] && [ "$_has_backreference" != true ]; then exit 0; fi
|
|
337
|
+
|
|
289
338
|
# ── Pattern matching ──────────────────────────────────────────────────────────
|
|
290
339
|
|
|
291
340
|
while IFS=$'\t' read -r pattern reason exclude || [ -n "$pattern" ]; do
|
|
@@ -320,6 +369,7 @@ while IFS=$'\t' read -r pattern reason exclude || [ -n "$pattern" ]; do
|
|
|
320
369
|
|
|
321
370
|
if [ "$matched" = "true" ]; then
|
|
322
371
|
echo "[pre-bash-guard] ${reason:-Command blocked by pre-bash-guard}" >&2
|
|
372
|
+
doflow_decide deny blocked-pattern
|
|
323
373
|
exit 2
|
|
324
374
|
fi
|
|
325
375
|
done < "$PATTERNS_FILE"
|