@aldegad/safedeps 2.5.1 → 2.6.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.
- package/README.ko.md +2 -0
- package/README.md +1 -1
- package/bin/safedeps +41 -41
- package/package.json +1 -1
- package/scripts/safedeps-post-verify.sh +6 -6
- package/scripts/test/e2e.sh +2 -2
- package/scripts/test/smoke.sh +1 -1
package/README.ko.md
CHANGED
|
@@ -22,6 +22,8 @@ cd "$(npm root -g)/@aldegad/safedeps" && node scripts/install/install-safedeps-h
|
|
|
22
22
|
|
|
23
23
|
> `safedeps` 는 CLI 명령어이고, npm 패키지는 **`@aldegad/safedeps`** 다 — npm 의 unscoped `safedeps` 는 무관한 남의 패키지. 전체 skill 소스 트리를 원하면 [설치](#설치) 참고.
|
|
24
24
|
|
|
25
|
+

|
|
26
|
+
|
|
25
27
|
*Detailed reference → [README.md](./README.md) (영문, SSoT)*
|
|
26
28
|
|
|
27
29
|
---
|
package/README.md
CHANGED
|
@@ -22,7 +22,7 @@ cd "$(npm root -g)/@aldegad/safedeps" && node scripts/install/install-safedeps-h
|
|
|
22
22
|
|
|
23
23
|
> `safedeps` is the CLI command; the npm package is **`@aldegad/safedeps`** — the unscoped `safedeps` on npm is an unrelated package. Prefer the full skill source tree? See [Installation](#installation).
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+

|
|
26
26
|
|
|
27
27
|
## Distribution Model
|
|
28
28
|
|
package/bin/safedeps
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
set -euo pipefail
|
|
9
9
|
|
|
10
|
-
SAFEDEPS_VERSION="2.
|
|
10
|
+
SAFEDEPS_VERSION="2.6.0"
|
|
11
11
|
|
|
12
12
|
# ---- repo / lib bootstrap ----------------------------------------------------
|
|
13
13
|
|
|
@@ -340,7 +340,7 @@ sf_cmd_check_npm_full_closure() {
|
|
|
340
340
|
evidence_file=$(sf_mktemp_evidence)
|
|
341
341
|
transitive_file=$(sf_closure_temp_file)
|
|
342
342
|
|
|
343
|
-
sf_spinner_start "npm closure
|
|
343
|
+
sf_spinner_start "resolving npm closure (${pkg}@${version})"
|
|
344
344
|
if ! sf_npm_closure_for_spec "${pkg}" "${version}" "${closure_file}"; then
|
|
345
345
|
sf_spinner_stop
|
|
346
346
|
rm -f "${closure_file}" "${batch_file}" "${evidence_file}" "${transitive_file}"
|
|
@@ -352,10 +352,10 @@ sf_cmd_check_npm_full_closure() {
|
|
|
352
352
|
fi
|
|
353
353
|
sf_spinner_stop
|
|
354
354
|
|
|
355
|
-
sf_spinner_start "closure
|
|
355
|
+
sf_spinner_start "batch-checking closure for advisories (OSV / KEV)"
|
|
356
356
|
if ! sf_npm_batch_check_closure "${closure_file}" "${batch_file}"; then
|
|
357
357
|
sf_spinner_stop
|
|
358
|
-
sf_err "OSV batch
|
|
358
|
+
sf_err "no OSV batch response — fail-closed (closure cache miss + live query failed)"
|
|
359
359
|
sf_advisory_log "check fail-closed npm-closure package=${pkg} version=${version}"
|
|
360
360
|
rm -f "${closure_file}" "${batch_file}" "${evidence_file}" "${transitive_file}"
|
|
361
361
|
if [[ "${SAFEDEPS_JSON_MODE}" -eq 1 ]]; then
|
|
@@ -377,7 +377,7 @@ sf_cmd_check_npm_full_closure() {
|
|
|
377
377
|
if [[ "${kev_count}" -gt 0 ]]; then
|
|
378
378
|
local kev_summary
|
|
379
379
|
kev_summary=$(jq -r '[.[] | select(.status == "hard_block") | "\(.package)@\(.version)" + (if .direct then " (direct)" else " (transitive)" end)] | join(", ")' "${batch_file}")
|
|
380
|
-
sf_err "KEV
|
|
380
|
+
sf_err "KEV-matched package in closure — install blocked: ${kev_summary}"
|
|
381
381
|
sf_advisory_log "check block(KEV closure) package=${pkg} version=${version} affected=${kev_summary}"
|
|
382
382
|
if [[ "${SAFEDEPS_JSON_MODE}" -eq 1 ]]; then
|
|
383
383
|
sf_npm_emit_closure_block_json "kev_hard_block" "${ecosystem}" "${pkg}" "${range}" "${version}" "${batch_file}"
|
|
@@ -402,7 +402,7 @@ sf_cmd_check_npm_full_closure() {
|
|
|
402
402
|
fi
|
|
403
403
|
|
|
404
404
|
for patched_version in "${patched_versions[@]+${patched_versions[@]}}"; do
|
|
405
|
-
sf_warn "${pkg}@${version} direct
|
|
405
|
+
sf_warn "${pkg}@${version} direct vulnerable — re-checking closure for candidate ${patched_version}."
|
|
406
406
|
if ! sf_npm_closure_for_spec "${pkg}" "${patched_version}" "${closure_file}"; then
|
|
407
407
|
continue
|
|
408
408
|
fi
|
|
@@ -422,7 +422,7 @@ sf_cmd_check_npm_full_closure() {
|
|
|
422
422
|
local hash; hash=$(jq -r '.hash' <<< "${spec_json}")
|
|
423
423
|
local expires_at; expires_at=$(jq -r '.expires_at' <<< "${spec_json}")
|
|
424
424
|
local transitive_count; transitive_count=$(jq 'length' "${transitive_file}")
|
|
425
|
-
sf_ok "${pkg}@${patched_version} full closure
|
|
425
|
+
sf_ok "${pkg}@${patched_version} full closure cleared (transitive ${transitive_count}, until ${expires_at})"
|
|
426
426
|
sf_info "ledger: ${hash}"
|
|
427
427
|
sf_advisory_log "check approve(patched closure) package=${pkg} version=${patched_version} hash=${hash} transitive=${transitive_count} prev_version=${version}"
|
|
428
428
|
rm -f "${direct_evidence}" "${closure_file}" "${batch_file}" "${evidence_file}" "${transitive_file}"
|
|
@@ -443,7 +443,7 @@ sf_cmd_check_npm_full_closure() {
|
|
|
443
443
|
|
|
444
444
|
local affected_summary
|
|
445
445
|
affected_summary=$(jq -r '[.[] | select(.status == "vulnerable") | "\(.package)@\(.version)" + (if .direct then " (direct)" else " (transitive)" end)] | join(", ")' "${batch_file}")
|
|
446
|
-
sf_warn "
|
|
446
|
+
sf_warn "vulnerable package in closure — approval withheld: ${affected_summary}"
|
|
447
447
|
sf_advisory_log "check block(vulnerable closure) package=${pkg} version=${version} affected=${affected_summary}"
|
|
448
448
|
if [[ "${SAFEDEPS_JSON_MODE}" -eq 1 ]]; then
|
|
449
449
|
sf_npm_emit_closure_block_json "${block_result}" "${ecosystem}" "${pkg}" "${range}" "${version}" "${batch_file}"
|
|
@@ -459,7 +459,7 @@ sf_cmd_check_npm_full_closure() {
|
|
|
459
459
|
local hash; hash=$(jq -r '.hash' <<< "${spec_json}")
|
|
460
460
|
local expires_at; expires_at=$(jq -r '.expires_at' <<< "${spec_json}")
|
|
461
461
|
local transitive_count; transitive_count=$(jq 'length' "${transitive_file}")
|
|
462
|
-
sf_ok "${pkg}@${version} full closure
|
|
462
|
+
sf_ok "${pkg}@${version} full closure cleared (transitive ${transitive_count}, until ${expires_at})"
|
|
463
463
|
sf_info "ledger: ${hash}"
|
|
464
464
|
sf_advisory_log "check approve(clean closure) package=${pkg} version=${version} hash=${hash} transitive=${transitive_count}"
|
|
465
465
|
rm -f "${closure_file}" "${batch_file}" "${evidence_file}" "${transitive_file}"
|
|
@@ -506,10 +506,10 @@ cmd_check() {
|
|
|
506
506
|
range=$(sf_parse_pkg_spec "${pkg_spec}" | sed -n '2p')
|
|
507
507
|
|
|
508
508
|
local version
|
|
509
|
-
sf_spinner_start "
|
|
509
|
+
sf_spinner_start "resolving version (${pkg}@${range})"
|
|
510
510
|
if ! version=$(sf_resolve_version "${ecosystem}" "${pkg}" "${range}"); then
|
|
511
511
|
sf_spinner_stop
|
|
512
|
-
sf_err "
|
|
512
|
+
sf_err "version resolution failed: ${pkg}@${range}"
|
|
513
513
|
if [[ "${SAFEDEPS_JSON_MODE}" -eq 1 ]]; then
|
|
514
514
|
jq -nc --arg ecosystem "${ecosystem}" --arg package "${pkg}" --arg range "${range}" \
|
|
515
515
|
'{command:"check", ecosystem:$ecosystem, package:$package, input_range:$range, result:"error", error:"version_resolution_failed"}'
|
|
@@ -526,7 +526,7 @@ cmd_check() {
|
|
|
526
526
|
hash=$(jq -r '.hash' <<< "${ledger_check}")
|
|
527
527
|
approved_at=$(jq -r '.spec.approved_at // "n/a"' <<< "${ledger_check}")
|
|
528
528
|
expires_at=$(jq -r '.spec.expires_at // "n/a"' <<< "${ledger_check}")
|
|
529
|
-
sf_ok "${pkg}@${version}
|
|
529
|
+
sf_ok "${pkg}@${version} already approved (until ${expires_at})"
|
|
530
530
|
sf_info "ledger: ${hash}"
|
|
531
531
|
if [[ "${SAFEDEPS_JSON_MODE}" -eq 1 ]]; then
|
|
532
532
|
jq -nc \
|
|
@@ -551,10 +551,10 @@ cmd_check() {
|
|
|
551
551
|
|
|
552
552
|
# Provider query (canonical truth = OSV; KEV overlay; GHSA enrichment)
|
|
553
553
|
local provider_json
|
|
554
|
-
sf_spinner_start "
|
|
554
|
+
sf_spinner_start "checking advisories (OSV / KEV / GHSA)"
|
|
555
555
|
if ! provider_json=$(safedeps_providers_query "${ecosystem}" "${pkg}" "${version}"); then
|
|
556
556
|
sf_spinner_stop
|
|
557
|
-
sf_err "OSV primary
|
|
557
|
+
sf_err "no OSV primary response — fail-closed (cache miss + live query failed)"
|
|
558
558
|
sf_advisory_log "check fail-closed ecosystem=${ecosystem} package=${pkg} version=${version}"
|
|
559
559
|
if [[ "${SAFEDEPS_JSON_MODE}" -eq 1 ]]; then
|
|
560
560
|
jq -nc \
|
|
@@ -577,11 +577,11 @@ cmd_check() {
|
|
|
577
577
|
|
|
578
578
|
case "${status}" in
|
|
579
579
|
hard_block)
|
|
580
|
-
sf_err "KEV
|
|
580
|
+
sf_err "KEV match — ${pkg}@${version} is exploited in the wild. install blocked."
|
|
581
581
|
local kev_cves
|
|
582
582
|
kev_cves=$(jq -r '[.kev.matches[]?.cveID] | unique | join(", ")' <<< "${provider_json}")
|
|
583
|
-
[[ -n "${kev_cves}" ]] && sf_info "
|
|
584
|
-
sf_warn "
|
|
583
|
+
[[ -n "${kev_cves}" ]] && sf_info "related CVEs: ${kev_cves}"
|
|
584
|
+
sf_warn "consider an alternative package; this spec is not approved in the ledger."
|
|
585
585
|
sf_advisory_log "check block(KEV) ecosystem=${ecosystem} package=${pkg} version=${version} cves=${kev_cves}"
|
|
586
586
|
if [[ "${SAFEDEPS_JSON_MODE}" -eq 1 ]]; then
|
|
587
587
|
jq -c \
|
|
@@ -610,11 +610,11 @@ cmd_check() {
|
|
|
610
610
|
|
|
611
611
|
for patched_version in "${patched_versions[@]}"; do
|
|
612
612
|
last_patched="${patched_version}"
|
|
613
|
-
sf_warn "${pkg}@${version}
|
|
614
|
-
sf_spinner_start "${patched_version}
|
|
613
|
+
sf_warn "${pkg}@${version} has ${vuln_count} CVE(s) — re-checking candidate ${patched_version}."
|
|
614
|
+
sf_spinner_start "re-checking ${patched_version}"
|
|
615
615
|
if ! narrow_json=$(safedeps_providers_query "${ecosystem}" "${pkg}" "${patched_version}"); then
|
|
616
616
|
sf_spinner_stop
|
|
617
|
-
sf_err "${pkg}@${patched_version}
|
|
617
|
+
sf_err "${pkg}@${patched_version} re-check failed — fail-closed"
|
|
618
618
|
rm -f "${tmp_evidence}"
|
|
619
619
|
if [[ "${SAFEDEPS_JSON_MODE}" -eq 1 ]]; then
|
|
620
620
|
jq -nc \
|
|
@@ -630,7 +630,7 @@ cmd_check() {
|
|
|
630
630
|
narrow_status=$(jq -r '.status' <<< "${narrow_json}")
|
|
631
631
|
last_status="${narrow_status}"
|
|
632
632
|
if [[ "${narrow_status}" != "clean" ]]; then
|
|
633
|
-
sf_warn "
|
|
633
|
+
sf_warn "patch candidate ${patched_version} is not clean either (status=${narrow_status}); trying the next candidate."
|
|
634
634
|
continue
|
|
635
635
|
fi
|
|
636
636
|
|
|
@@ -644,7 +644,7 @@ cmd_check() {
|
|
|
644
644
|
rm -f "${narrow_evidence}" "${tmp_evidence}"
|
|
645
645
|
local hash; hash=$(jq -r '.hash' <<< "${spec_json}")
|
|
646
646
|
local expires_at; expires_at=$(jq -r '.expires_at' <<< "${spec_json}")
|
|
647
|
-
sf_ok "${pkg}@${patched_version}
|
|
647
|
+
sf_ok "${pkg}@${patched_version} approved (until ${expires_at})"
|
|
648
648
|
sf_info "ledger: ${hash}"
|
|
649
649
|
sf_advisory_log "check approve(patched) ecosystem=${ecosystem} package=${pkg} version=${patched_version} hash=${hash} prev_version=${version}"
|
|
650
650
|
if [[ "${SAFEDEPS_JSON_MODE}" -eq 1 ]]; then
|
|
@@ -658,7 +658,7 @@ cmd_check() {
|
|
|
658
658
|
return 0
|
|
659
659
|
done
|
|
660
660
|
|
|
661
|
-
sf_err "
|
|
661
|
+
sf_err "re-checked all patch candidates but none are clean (last=${last_patched}, status=${last_status})"
|
|
662
662
|
rm -f "${tmp_evidence}"
|
|
663
663
|
if [[ "${SAFEDEPS_JSON_MODE}" -eq 1 ]]; then
|
|
664
664
|
jq -nc \
|
|
@@ -669,7 +669,7 @@ cmd_check() {
|
|
|
669
669
|
fi
|
|
670
670
|
return 2
|
|
671
671
|
else
|
|
672
|
-
sf_warn "${pkg}@${version}
|
|
672
|
+
sf_warn "${pkg}@${version} has ${vuln_count} CVE(s) — no patch available. approval withheld."
|
|
673
673
|
sf_advisory_log "check warn(no-patch) ecosystem=${ecosystem} package=${pkg} version=${version} vulns=${vuln_count}"
|
|
674
674
|
rm -f "${tmp_evidence}"
|
|
675
675
|
if [[ "${SAFEDEPS_JSON_MODE}" -eq 1 ]]; then
|
|
@@ -688,7 +688,7 @@ cmd_check() {
|
|
|
688
688
|
rm -f "${tmp_evidence}"
|
|
689
689
|
local hash; hash=$(jq -r '.hash' <<< "${spec_json}")
|
|
690
690
|
local expires_at; expires_at=$(jq -r '.expires_at' <<< "${spec_json}")
|
|
691
|
-
sf_ok "${pkg}@${version}
|
|
691
|
+
sf_ok "${pkg}@${version} approved (until ${expires_at})"
|
|
692
692
|
sf_info "ledger: ${hash}"
|
|
693
693
|
sf_advisory_log "check approve(clean) ecosystem=${ecosystem} package=${pkg} version=${version} hash=${hash}"
|
|
694
694
|
if [[ "${SAFEDEPS_JSON_MODE}" -eq 1 ]]; then
|
|
@@ -702,7 +702,7 @@ cmd_check() {
|
|
|
702
702
|
;;
|
|
703
703
|
|
|
704
704
|
*)
|
|
705
|
-
sf_err "
|
|
705
|
+
sf_err "unexpected provider status: ${status}"
|
|
706
706
|
rm -f "${tmp_evidence}"
|
|
707
707
|
return 4
|
|
708
708
|
;;
|
|
@@ -757,7 +757,7 @@ cmd_ledger() {
|
|
|
757
757
|
fi
|
|
758
758
|
|
|
759
759
|
if [[ ${#entries[@]} -eq 0 ]]; then
|
|
760
|
-
sf_info "approved-specs
|
|
760
|
+
sf_info "approved-specs is empty (${SAFEDEPS_LEDGER_DIR})"
|
|
761
761
|
return 0
|
|
762
762
|
fi
|
|
763
763
|
|
|
@@ -830,7 +830,7 @@ cmd_revoke() {
|
|
|
830
830
|
local target_file=""
|
|
831
831
|
if [[ "${arg1}" == sha256:* ]]; then
|
|
832
832
|
target_file=$(safedeps_ledger_path_for_hash "${arg1}")
|
|
833
|
-
[[ -f "${target_file}" ]] || { sf_err "ledger entry
|
|
833
|
+
[[ -f "${target_file}" ]] || { sf_err "no ledger entry: ${arg1}"; return 1; }
|
|
834
834
|
else
|
|
835
835
|
# one or two args. Two = ecosystem + pkg@version. One = pkg@version (scan).
|
|
836
836
|
if [[ -n "${arg2}" ]]; then
|
|
@@ -839,7 +839,7 @@ cmd_revoke() {
|
|
|
839
839
|
version=$(sf_parse_pkg_spec "${arg2}" | sed -n '2p')
|
|
840
840
|
[[ -n "${version}" ]] || { sf_eprintf "safedeps: revoke needs pkg@version, got '${arg2}'"; return 4; }
|
|
841
841
|
target_file=$(safedeps_ledger_path "${arg1}" "${pkg}" "${version}")
|
|
842
|
-
[[ -f "${target_file}" ]] || { sf_err "ledger entry
|
|
842
|
+
[[ -f "${target_file}" ]] || { sf_err "no ledger entry: ${arg1} ${pkg}@${version}"; return 1; }
|
|
843
843
|
else
|
|
844
844
|
local pkg version
|
|
845
845
|
pkg=$(sf_parse_pkg_spec "${arg1}" | sed -n '1p')
|
|
@@ -855,9 +855,9 @@ cmd_revoke() {
|
|
|
855
855
|
fi
|
|
856
856
|
done < <(find "${SAFEDEPS_LEDGER_DIR}" -maxdepth 1 -name '*.json' -type f -print0 2>/dev/null)
|
|
857
857
|
case "${#matches[@]}" in
|
|
858
|
-
0) sf_err "ledger entry
|
|
858
|
+
0) sf_err "no ledger entry: ${pkg}@${version}"; return 1 ;;
|
|
859
859
|
1) target_file="${matches[0]}" ;;
|
|
860
|
-
*) sf_err "${pkg}@${version}
|
|
860
|
+
*) sf_err "${pkg}@${version} matched in multiple ecosystems — specify the ecosystem"; return 4 ;;
|
|
861
861
|
esac
|
|
862
862
|
fi
|
|
863
863
|
fi
|
|
@@ -870,7 +870,7 @@ cmd_revoke() {
|
|
|
870
870
|
local revoked_json
|
|
871
871
|
revoked_json=$(safedeps_ledger_revoke "${ecosystem}" "${pkg}" "${version}" "${reason}")
|
|
872
872
|
sf_advisory_log "revoke ecosystem=${ecosystem} package=${pkg} version=${version} reason=${reason}"
|
|
873
|
-
sf_ok "
|
|
873
|
+
sf_ok "revoked: ${ecosystem} ${pkg}@${version}"
|
|
874
874
|
sf_info "reason: ${reason}"
|
|
875
875
|
|
|
876
876
|
if [[ "${SAFEDEPS_JSON_MODE}" -eq 1 ]]; then
|
|
@@ -909,17 +909,17 @@ cmd_recheck() {
|
|
|
909
909
|
hash=$(jq -r '.hash // ""' "${f}")
|
|
910
910
|
[[ -n "${revoked_at}" ]] && continue
|
|
911
911
|
if [[ -f "${SAFEDEPS_ADVISORY_LOG}" ]] && ! sf_ledger_has_approval_provenance "${hash}" "${ecosystem}" "${pkg}" "${version}"; then
|
|
912
|
-
sf_warn " provenance
|
|
912
|
+
sf_warn " no provenance — flagged as suspected forgery (not revoked)"
|
|
913
913
|
suspected_forgery_arr=$(jq -c \
|
|
914
914
|
--arg ecosystem "${ecosystem}" --arg package "${pkg}" --arg version "${version}" --arg hash "${hash}" \
|
|
915
915
|
'. + [{ecosystem:$ecosystem, package:$package, version:$version, hash:$hash, reason:"missing_advisory_log_approval"}]' <<< "${suspected_forgery_arr}")
|
|
916
916
|
fi
|
|
917
917
|
checked=$(( checked + 1 ))
|
|
918
918
|
|
|
919
|
-
sf_info "
|
|
919
|
+
sf_info "re-checking ${ecosystem} ${pkg}@${version}"
|
|
920
920
|
local pj
|
|
921
921
|
if ! pj=$(safedeps_providers_query "${ecosystem}" "${pkg}" "${version}" 2>/dev/null); then
|
|
922
|
-
sf_warn " provider
|
|
922
|
+
sf_warn " no provider response — skipped"
|
|
923
923
|
continue
|
|
924
924
|
fi
|
|
925
925
|
local s; s=$(jq -r '.status' <<< "${pj}")
|
|
@@ -932,12 +932,12 @@ cmd_recheck() {
|
|
|
932
932
|
safedeps_ledger_revoke "${ecosystem}" "${pkg}" "${version}" "${reason}" >/dev/null
|
|
933
933
|
sf_advisory_log "re-check revoke ecosystem=${ecosystem} package=${pkg} version=${version} status=${s}"
|
|
934
934
|
if [[ "${s}" == "hard_block" ]]; then
|
|
935
|
-
sf_err " KEV
|
|
935
|
+
sf_err " KEV match -> revoked"
|
|
936
936
|
kev_hit_arr=$(jq -c \
|
|
937
937
|
--arg ecosystem "${ecosystem}" --arg package "${pkg}" --arg version "${version}" \
|
|
938
938
|
'. + [{ecosystem:$ecosystem, package:$package, version:$version, status:"hard_block"}]' <<< "${kev_hit_arr}")
|
|
939
939
|
else
|
|
940
|
-
sf_warn "
|
|
940
|
+
sf_warn " new CVE match -> revoked"
|
|
941
941
|
newly_vuln_arr=$(jq -c \
|
|
942
942
|
--arg ecosystem "${ecosystem}" --arg package "${pkg}" --arg version "${version}" \
|
|
943
943
|
'. + [{ecosystem:$ecosystem, package:$package, version:$version, status:"vulnerable"}]' <<< "${newly_vuln_arr}")
|
|
@@ -961,15 +961,15 @@ cmd_recheck() {
|
|
|
961
961
|
return 0
|
|
962
962
|
fi
|
|
963
963
|
|
|
964
|
-
sf_info "
|
|
964
|
+
sf_info "re-check complete: ${still_clean}/${checked} still clean"
|
|
965
965
|
local nv kv
|
|
966
966
|
nv=$(jq -r 'length' <<< "${newly_vuln_arr}")
|
|
967
967
|
kv=$(jq -r 'length' <<< "${kev_hit_arr}")
|
|
968
968
|
local fg
|
|
969
969
|
fg=$(jq -r 'length' <<< "${suspected_forgery_arr}")
|
|
970
|
-
[[ "${nv}" -gt 0 ]] && sf_warn "
|
|
971
|
-
[[ "${kv}" -gt 0 ]] && sf_err "
|
|
972
|
-
[[ "${fg}" -gt 0 ]] && sf_warn "
|
|
970
|
+
[[ "${nv}" -gt 0 ]] && sf_warn "revoked ${nv} for new CVE match"
|
|
971
|
+
[[ "${kv}" -gt 0 ]] && sf_err "revoked ${kv} for KEV match"
|
|
972
|
+
[[ "${fg}" -gt 0 ]] && sf_warn "flagged ${fg} ledger entries with no approval provenance"
|
|
973
973
|
}
|
|
974
974
|
|
|
975
975
|
# ---- migrate -----------------------------------------------------------------
|
package/package.json
CHANGED
|
@@ -781,12 +781,12 @@ LOG_EOF
|
|
|
781
781
|
--arg log_path "${GUARD_DIR}/reorg.log" \
|
|
782
782
|
'{
|
|
783
783
|
systemMessage: (
|
|
784
|
-
"safedeps:
|
|
785
|
-
"
|
|
786
|
-
"
|
|
787
|
-
"
|
|
788
|
-
(if $warnings == "" then "" else "\n\
|
|
789
|
-
"\n\
|
|
784
|
+
"safedeps: suspicious dependency change detected — rolled back to the last confirmed safe snapshot.\n\n" +
|
|
785
|
+
"Detected problems:\n" + $reasons + "\n\n" +
|
|
786
|
+
"Rollback snapshot: " + $rollback_snapshot + "\n" +
|
|
787
|
+
"Rolled-back files: " + $rolled_back +
|
|
788
|
+
(if $warnings == "" then "" else "\n\nAdditional warnings:\n" + $warnings end) +
|
|
789
|
+
"\n\nDetails log: " + $log_path
|
|
790
790
|
)
|
|
791
791
|
}'
|
|
792
792
|
exit 0
|
package/scripts/test/e2e.sh
CHANGED
|
@@ -230,7 +230,7 @@ revert_post=$(
|
|
|
230
230
|
{"tool_name":"Bash","tool_input":{"command":"npm install fixture-parent@1.0.0"},"cwd":"${revert_project}"}
|
|
231
231
|
EOF
|
|
232
232
|
)
|
|
233
|
-
grep -q '
|
|
233
|
+
grep -q 'suspicious dependency change detected' <<< "${revert_post}" || fail "reorg fires on a tampered lockfile"
|
|
234
234
|
cmp -s "${revert_project}/package-lock.json" "${tmp_root}/revert-safe-lock.json" || fail "reorg restores the exact safe lockfile content on disk"
|
|
235
235
|
pass "reorg reverts a tampered lockfile to safe content on disk"
|
|
236
236
|
|
|
@@ -267,7 +267,7 @@ missing_post=$(
|
|
|
267
267
|
{"tool_name":"Bash","tool_input":{"command":"npm install fixture-parent@1.0.0"},"cwd":"${missing_project}"}
|
|
268
268
|
EOF
|
|
269
269
|
)
|
|
270
|
-
grep -q '
|
|
270
|
+
grep -q 'suspicious dependency change detected' <<< "${missing_post}" || fail "post hook reorgs unapproved transitive package"
|
|
271
271
|
grep -q 'fixture-child@1.0.0' <<< "${missing_post}" || fail "post hook names unapproved transitive package"
|
|
272
272
|
# Not just the message — the unapproved transitive must be gone from the on-disk
|
|
273
273
|
# lockfile. (Reorg removes the tampered lockfile; a no-network reinstall may recreate
|
package/scripts/test/smoke.sh
CHANGED
|
@@ -262,7 +262,7 @@ tamper_post=$(
|
|
|
262
262
|
jq -nc --arg cwd "${project_dir}" '{tool_name:"Bash",tool_input:{command:"npm install ledger-tamper@1.0.0"},cwd:$cwd}' |
|
|
263
263
|
HOME="${tamper_home}" SAFEDEPS_HOME="${tamper_safe}" scripts/safedeps-post-verify.sh
|
|
264
264
|
)
|
|
265
|
-
grep -q '
|
|
265
|
+
grep -q 'suspicious dependency change detected' <<< "${tamper_post}" || fail "post hook reorgs safedeps ledger tamper script"
|
|
266
266
|
pass "post hook reorgs safedeps ledger tamper script"
|
|
267
267
|
|
|
268
268
|
fixture_json="${tmp_root}/recheck-fixture.json"
|