@aldegad/safedeps 2.1.1 → 2.2.0

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.
@@ -22,9 +22,15 @@ trap cleanup EXIT
22
22
  bash -n bin/safedeps
23
23
  bash -n lib/providers/providers.sh
24
24
  bash -n lib/ledger/ledger.sh
25
+ bash -n lib/npm/closure.sh
25
26
  bash -n scripts/safedeps-pre-guard.sh
26
27
  bash -n scripts/safedeps-post-verify.sh
27
28
  bash -n scripts/safedeps-recheck-alert.sh
29
+ bash -n scripts/release-gates.sh
30
+ bash -n lib/gates/repo-profile.sh
31
+ bash -n lib/gates/scan.sh
32
+ bash -n lib/gates/audit.sh
33
+ bash -n lib/gates/hooks.sh
28
34
  pass "bash syntax"
29
35
 
30
36
  node --check scripts/install/install-safedeps-hooks.mjs >/dev/null
@@ -35,7 +41,8 @@ node scripts/install/install-safedeps-recheck-agent.mjs --help >/dev/null
35
41
  pass "node syntax"
36
42
 
37
43
  version_json=$(HOME="${tmp_root}/home-version" SAFEDEPS_HOME="${tmp_root}/safe-version" ./bin/safedeps --json version)
38
- [[ "$(jq -r '.version' <<< "${version_json}")" == "2.1.0" ]] || fail "version json is 2.1.0"
44
+ pkg_version=$(jq -r '.version' package.json)
45
+ [[ "$(jq -r '.version' <<< "${version_json}")" == "${pkg_version}" ]] || fail "cli version matches package.json (${pkg_version})"
39
46
  pass "cli version"
40
47
 
41
48
  ledger_json=$(HOME="${tmp_root}/home-ledger" SAFEDEPS_HOME="${tmp_root}/safe-ledger" ./bin/safedeps --json ledger)
@@ -55,11 +62,28 @@ project_dir="${tmp_root}/project"
55
62
  mkdir -p "${project_dir}"
56
63
  printf '{"dependencies":{}}\n' > "${project_dir}/package.json"
57
64
 
65
+ run_hook_command() {
66
+ local home_dir="$1"
67
+ local safe_dir="$2"
68
+ local command="$3"
69
+
70
+ jq -nc --arg command "${command}" --arg cwd "${project_dir}" \
71
+ '{tool_name:"Bash",tool_input:{command:$command},cwd:$cwd}' |
72
+ HOME="${home_dir}" SAFEDEPS_HOME="${safe_dir}" scripts/safedeps-pre-guard.sh
73
+ }
74
+
75
+ run_codex_hook_command() {
76
+ local home_dir="$1"
77
+ local safe_dir="$2"
78
+ local command="$3"
79
+
80
+ jq -nc --arg command "${command}" --arg cwd "${project_dir}" \
81
+ '{tool_name:"Bash",tool_input:{command:$command},cwd:$cwd,turn_id:"turn-smoke",model:"codex-test"}' |
82
+ HOME="${home_dir}" SAFEDEPS_HOME="${safe_dir}" scripts/safedeps-pre-guard.sh
83
+ }
84
+
58
85
  deny_json=$(
59
- HOME="${tmp_root}/home-hook" SAFEDEPS_HOME="${tmp_root}/safe-hook" \
60
- scripts/safedeps-pre-guard.sh <<EOF
61
- {"tool_name":"Bash","tool_input":{"command":"npm install left-pad@1.3.0"},"cwd":"${project_dir}"}
62
- EOF
86
+ run_hook_command "${tmp_root}/home-hook" "${tmp_root}/safe-hook" "npm install left-pad@1.3.0"
63
87
  )
64
88
  [[ "$(jq -r '.hookSpecificOutput.permissionDecision' <<< "${deny_json}")" == "deny" ]] || fail "hook denies unapproved install"
65
89
  pass "hook denies unapproved install"
@@ -67,13 +91,103 @@ pass "hook denies unapproved install"
67
91
  mkdir -p "${tmp_root}/safe-hook-allow"
68
92
  SAFEDEPS_HOME="${tmp_root}/safe-hook-allow" lib/ledger/ledger.sh approve npm left-pad 1.3.0 1.3.0 smoke >/dev/null
69
93
  allow_output=$(
70
- HOME="${tmp_root}/home-hook-allow" SAFEDEPS_HOME="${tmp_root}/safe-hook-allow" \
71
- scripts/safedeps-pre-guard.sh <<EOF
72
- {"tool_name":"Bash","tool_input":{"command":"npm install left-pad@1.3.0"},"cwd":"${project_dir}"}
94
+ run_hook_command "${tmp_root}/home-hook-allow" "${tmp_root}/safe-hook-allow" "npm install left-pad@1.3.0"
95
+ )
96
+ [[ "$(jq -r '.hookSpecificOutput.permissionDecision' <<< "${allow_output}")" == "allow" ]] || fail "hook emits Claude allow decision for approved install"
97
+ [[ "$(jq -r '.hookSpecificOutput.updatedInput.command' <<< "${allow_output}")" == "npm install left-pad@1.3.0 --ignore-scripts" ]] || fail "hook injects --ignore-scripts for Claude npm install"
98
+ allow_sid=$(jq -r '.snapshot_id' "${tmp_root}/safe-hook-allow/current_state")
99
+ jq -e '.ignore_scripts_injected == true' "${tmp_root}/safe-hook-allow/snapshots/${allow_sid}_meta.json" >/dev/null || fail "hook records injected meta flag"
100
+ pass "hook injects --ignore-scripts for Claude approved install"
101
+
102
+ mkdir -p "${tmp_root}/safe-hook-codex"
103
+ SAFEDEPS_HOME="${tmp_root}/safe-hook-codex" lib/ledger/ledger.sh approve npm left-pad 1.3.0 1.3.0 smoke >/dev/null
104
+ codex_allow_output=$(
105
+ run_codex_hook_command "${tmp_root}/home-hook-codex" "${tmp_root}/safe-hook-codex" "npm install left-pad@1.3.0"
106
+ )
107
+ [[ -z "${codex_allow_output}" ]] || fail "hook keeps Codex approved install as plain allow"
108
+ codex_sid=$(jq -r '.snapshot_id' "${tmp_root}/safe-hook-codex/current_state")
109
+ jq -e '.ignore_scripts_injected == false' "${tmp_root}/safe-hook-codex/snapshots/${codex_sid}_meta.json" >/dev/null || fail "hook does not record injected meta flag for Codex"
110
+ pass "hook keeps Codex approved install as plain allow"
111
+
112
+ for inert_skip_cmd in "npm view left-pad" "npm run build" "npm --version"; do
113
+ inert_skip_output=$(run_hook_command "${tmp_root}/home-inert-skip" "${tmp_root}/safe-inert-skip" "${inert_skip_cmd}")
114
+ [[ -z "${inert_skip_output}" ]] || fail "hook does not inject non-install command: ${inert_skip_cmd}"
115
+ done
116
+ pass "hook does not inject npm non-install commands"
117
+
118
+ mkdir -p "${tmp_root}/safe-hook-ignore-scripts"
119
+ SAFEDEPS_HOME="${tmp_root}/safe-hook-ignore-scripts" lib/ledger/ledger.sh approve npm left-pad 1.3.0 1.3.0 smoke >/dev/null
120
+ ignore_scripts_output=$(
121
+ run_hook_command "${tmp_root}/home-hook-ignore-scripts" "${tmp_root}/safe-hook-ignore-scripts" "npm install left-pad@1.3.0 --ignore-scripts"
122
+ )
123
+ [[ -z "${ignore_scripts_output}" ]] || fail "hook does not duplicate --ignore-scripts"
124
+ ignore_sid=$(jq -r '.snapshot_id' "${tmp_root}/safe-hook-ignore-scripts/current_state")
125
+ jq -e '.ignore_scripts_injected == false' "${tmp_root}/safe-hook-ignore-scripts/snapshots/${ignore_sid}_meta.json" >/dev/null || fail "hook does not record injected meta flag when flag already exists"
126
+ pass "hook does not duplicate --ignore-scripts"
127
+
128
+ # Regression: `npx <tool> <args>` runs an already-installed binary. Arguments to
129
+ # the tool (e.g. an email) must NOT be misread as a pkg@spec install and denied.
130
+ npx_runner_output=$(
131
+ run_hook_command "${tmp_root}/home-npx-run" "${tmp_root}/safe-npx-run" "npx wrangler secret put ORIGIN_SHARED_SECRET --name pqc-auth-gateway dev1@block-s.io"
132
+ )
133
+ [[ -z "${npx_runner_output}" ]] || fail "hook allows npx tool run with @-bearing args"
134
+ pass "hook allows npx tool run with @-bearing args"
135
+
136
+ # Regression: a genuine install chained with an npx tool run must STILL be gated
137
+ # on the real package — and must not be polluted by the npx arg email.
138
+ mixed_output=$(
139
+ run_hook_command "${tmp_root}/home-mixed" "${tmp_root}/safe-mixed" "npm install evil-pkg@9.9.9 && npx wrangler secret put X dev1@block-s.io"
140
+ )
141
+ [[ "$(jq -r '.hookSpecificOutput.permissionDecision' <<< "${mixed_output}")" == "deny" ]] || fail "hook gates real install chained with npx run"
142
+ reason=$(jq -r '.hookSpecificOutput.permissionDecisionReason' <<< "${mixed_output}")
143
+ grep -q 'evil-pkg@9.9.9' <<< "${reason}" || fail "deny reason names the real package"
144
+ [[ "${reason}" != *"dev1@block-s.io"* ]] || fail "deny reason must not name the email arg"
145
+ pass "hook gates real install chained with npx run (email not polluted)"
146
+
147
+ echo_output=$(
148
+ run_hook_command "${tmp_root}/home-echo" "${tmp_root}/safe-echo" "echo \"npm install evil-pkg@9.9.9\""
149
+ )
150
+ [[ -z "${echo_output}" ]] || fail "hook ignores quoted echo text"
151
+ heredoc_output=$(
152
+ run_hook_command "${tmp_root}/home-heredoc" "${tmp_root}/safe-heredoc" $'cat <<'\''EOF'\''\nnpm install evil-pkg@9.9.9\nEOF'
153
+ )
154
+ [[ -z "${heredoc_output}" ]] || fail "hook ignores heredoc body text"
155
+ pass "hook ignores echo/heredoc text"
156
+
157
+ bypass_cases=(
158
+ "/usr/bin/npm install evil@1.2.3"
159
+ "bash -lc \"npm install evil@1.2.3\""
160
+ "env npm install evil@1.2.3"
161
+ "command npm install evil@1.2.3"
162
+ "npm --prefix sub install evil@1.2.3"
163
+ "pip install requests==2.31.0"
164
+ "gem install rails -v 7.1.0"
165
+ "cargo add serde --vers 1.0.0"
166
+ "dotnet add package X --version 1.0.0"
167
+ )
168
+ for bypass_cmd in "${bypass_cases[@]}"; do
169
+ bypass_output=$(run_hook_command "${tmp_root}/home-bypass" "${tmp_root}/safe-bypass" "${bypass_cmd}")
170
+ [[ "$(jq -r '.hookSpecificOutput.permissionDecision' <<< "${bypass_output}")" == "deny" ]] || fail "hook denies bypass: ${bypass_cmd}"
171
+ done
172
+ pass "hook denies install bypass forms"
173
+
174
+ tamper_safe="${tmp_root}/safe-tamper"
175
+ tamper_home="${tmp_root}/home-tamper"
176
+ SAFEDEPS_HOME="${tamper_safe}" lib/ledger/ledger.sh approve npm ledger-tamper 1.0.0 1.0.0 smoke >/dev/null
177
+ tamper_pre=$(run_hook_command "${tamper_home}" "${tamper_safe}" "npm install ledger-tamper@1.0.0")
178
+ [[ "$(jq -r '.hookSpecificOutput.permissionDecision' <<< "${tamper_pre}")" == "allow" ]] || fail "tamper fixture pre hook allows approved install"
179
+ mkdir -p "${project_dir}/node_modules/ledger-tamper"
180
+ jq '.dependencies["ledger-tamper"]="1.0.0"' "${project_dir}/package.json" > "${project_dir}/package.json.tmp"
181
+ mv "${project_dir}/package.json.tmp" "${project_dir}/package.json"
182
+ cat > "${project_dir}/node_modules/ledger-tamper/package.json" <<'EOF'
183
+ {"name":"ledger-tamper","version":"1.0.0","scripts":{"postinstall":"node -e \"require('fs').writeFileSync(process.env.HOME + '/.safedeps/approved-specs/evil.json', '{}')\""}}
73
184
  EOF
185
+ tamper_post=$(
186
+ jq -nc '{tool_name:"Bash",tool_input:{command:"npm install ledger-tamper@1.0.0"}}' |
187
+ HOME="${tamper_home}" SAFEDEPS_HOME="${tamper_safe}" scripts/safedeps-post-verify.sh
74
188
  )
75
- [[ -z "${allow_output}" ]] || fail "hook allows approved install"
76
- pass "hook allows approved install"
189
+ grep -q '의심스러운 패키지 변경 감지' <<< "${tamper_post}" || fail "post hook reorgs safedeps ledger tamper script"
190
+ pass "post hook reorgs safedeps ledger tamper script"
77
191
 
78
192
  fixture_json="${tmp_root}/recheck-fixture.json"
79
193
  printf '%s\n' '{"command":"re-check","checked":2,"still_clean":1,"newly_vulnerable":[],"kev_hit":[],"revoked":[]}' > "${fixture_json}"
@@ -86,4 +200,15 @@ grep -q '"checked":2' "${tmp_root}/safe-recheck/recheck.log" || fail "re-check w
86
200
  grep -q '"provider_skipped":1' "${tmp_root}/safe-recheck/recheck-alerts.jsonl" || fail "re-check wrapper alerts on skipped provider checks"
87
201
  pass "re-check alert wrapper"
88
202
 
203
+ # Release-time lane (absorbed from security-release-gates): commands must be
204
+ # registered and resolve their gate scripts.
205
+ gates_help=$(HOME="${tmp_root}/home-gates" SAFEDEPS_HOME="${tmp_root}/safe-gates" ./bin/safedeps help)
206
+ for gate_cmd in "gates" "scan secrets" "audit" "hooks"; do
207
+ grep -q "${gate_cmd}" <<< "${gates_help}" || fail "release-time command listed in help: ${gate_cmd}"
208
+ done
209
+ for gate_script in scripts/release-gates.sh lib/gates/repo-profile.sh lib/gates/scan.sh lib/gates/audit.sh lib/gates/hooks.sh; do
210
+ [[ -f "${gate_script}" ]] || fail "release-time gate script present: ${gate_script}"
211
+ done
212
+ pass "release-time gate commands registered"
213
+
89
214
  printf 'smoke passed\n'