From 7fb4e5b07bcf2249c420028e1b131767fae92a94 Mon Sep 17 00:00:00 2001 From: Shunichi Shinohara Date: Fri, 8 May 2026 12:24:18 +0900 Subject: [PATCH 01/10] feat(ci/diff-guard): wire per-target change rate threshold overrides vuls2 now accepts `--change-rate-threshold-override =` on both `vuls diff db` and `vuls diff detection`. Surface that surface through the diff-guard composite action and the db-nightly workflow so specific ecosystems / scan-result files can be relaxed without weakening the default threshold for the rest. The composite action gains two new optional inputs (`db_change_rate_threshold_overrides`, `detection_change_rate_threshold_overrides`). Each is a multi-line string of "=" entries; trailing `# ...` comments and blank lines are stripped at parse time. Each diff step expands the input into repeated `--change-rate-threshold-override` flags via a small bash loop. Empty input yields zero added args, so existing callers keep their bit-for-bit current behavior. The db-nightly workflow gets matching workflow_dispatch inputs with empty defaults; persistent overrides are intended to be added to those defaults via PR (with rationale in the PR description) rather than at dispatch time, so the workflow file itself becomes the single source of truth for which targets are relaxed and why. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/actions/diff-guard/action.yml | 56 ++++++++++++++++++++++++++- .github/workflows/db-nightly.yml | 31 ++++++++++++++- 2 files changed, 83 insertions(+), 4 deletions(-) diff --git a/.github/actions/diff-guard/action.yml b/.github/actions/diff-guard/action.yml index 174ee2d..1470e2c 100644 --- a/.github/actions/diff-guard/action.yml +++ b/.github/actions/diff-guard/action.yml @@ -66,11 +66,27 @@ inputs: description: OCI reference (repo:tag) to fetch the baseline vuls.db from. required: true db_change_rate_threshold: - description: "`vuls diff db` change rate (%) threshold per ecosystem." + description: "`vuls diff db` default change rate (%) threshold per ecosystem." required: true + db_change_rate_threshold_overrides: + description: | + Per-ecosystem overrides of the `vuls diff db` change rate threshold. + Multi-line "=" entries (e.g. "ubuntu:26.04=25"); blank + lines and trailing "#" comments are ignored. Empty value means "no + overrides" — every ecosystem uses db_change_rate_threshold. + required: false + default: "" detection_change_rate_threshold: - description: "`vuls diff detection` change rate (%) threshold per scan result." + description: "`vuls diff detection` default change rate (%) threshold per scan result." required: true + detection_change_rate_threshold_overrides: + description: | + Per-file overrides of the `vuls diff detection` change rate threshold. + Multi-line "=" entries (e.g. "debian_13=8") where + the basename is the scan-result file name without ".json"; blank lines + and trailing "#" comments are ignored. + required: false + default: "" integration_ref: description: vulsio/integration commit SHA to pin scan-result fixtures to. required: false @@ -153,17 +169,31 @@ runs: env: TARGET_DB: ${{ inputs.target_db }} DETECTION_CHANGE_RATE_THRESHOLD: ${{ inputs.detection_change_rate_threshold }} + DETECTION_CHANGE_RATE_THRESHOLD_OVERRIDES: ${{ inputs.detection_change_rate_threshold_overrides }} run: | # No `set -e` after `rc=$?` — the rest of the step must reach # the final `echo "rc=..." >> $GITHUB_OUTPUT` so the aggregator # can read it. set +e set -o pipefail + + # Expand the multi-line "=" input into a + # repeated `--change-rate-threshold-override =` flag. + # Blank lines and trailing "#" comments are skipped here; value + # validation (numeric, key non-empty, etc.) happens in vuls2. + overrides_args=() + while IFS= read -r line; do + line="${line%%#*}" + [[ "$line" =~ ^[[:space:]]*$ ]] && continue + overrides_args+=(--change-rate-threshold-override "$line") + done <<< "$DETECTION_CHANGE_RATE_THRESHOLD_OVERRIDES" + vuls diff detection \ ./scan-results \ ./baseline.db "$VULS0" \ "$TARGET_DB" "$VULS0" \ --change-rate-threshold "$DETECTION_CHANGE_RATE_THRESHOLD" \ + "${overrides_args[@]}" \ | tee ./diff-detection-report.md rc=$? @@ -225,16 +255,27 @@ runs: env: TARGET_DB: ${{ inputs.target_db }} DETECTION_CHANGE_RATE_THRESHOLD: ${{ inputs.detection_change_rate_threshold }} + DETECTION_CHANGE_RATE_THRESHOLD_OVERRIDES: ${{ inputs.detection_change_rate_threshold_overrides }} VULS0_OLD_REF: ${{ inputs.vuls0_old_ref }} run: | # Stay in `set +e` for the entire step. See note above. set +e set -o pipefail + + # See diff_detection_master for the override-array build rationale. + overrides_args=() + while IFS= read -r line; do + line="${line%%#*}" + [[ "$line" =~ ^[[:space:]]*$ ]] && continue + overrides_args+=(--change-rate-threshold-override "$line") + done <<< "$DETECTION_CHANGE_RATE_THRESHOLD_OVERRIDES" + vuls diff detection \ ./scan-results-old \ ./baseline.db "$VULS0_OLD" \ "$TARGET_DB" "$VULS0_OLD" \ --change-rate-threshold "$DETECTION_CHANGE_RATE_THRESHOLD" \ + "${overrides_args[@]}" \ | tee ./diff-detection-old-report.md rc=$? @@ -256,12 +297,23 @@ runs: env: TARGET_DB: ${{ inputs.target_db }} DB_CHANGE_RATE_THRESHOLD: ${{ inputs.db_change_rate_threshold }} + DB_CHANGE_RATE_THRESHOLD_OVERRIDES: ${{ inputs.db_change_rate_threshold_overrides }} run: | # Stay in `set +e` for the entire step. See note above. set +e set -o pipefail + + # See diff_detection_master for the override-array build rationale. + overrides_args=() + while IFS= read -r line; do + line="${line%%#*}" + [[ "$line" =~ ^[[:space:]]*$ ]] && continue + overrides_args+=(--change-rate-threshold-override "$line") + done <<< "$DB_CHANGE_RATE_THRESHOLD_OVERRIDES" + vuls diff db ./baseline.db "$TARGET_DB" \ --change-rate-threshold "$DB_CHANGE_RATE_THRESHOLD" \ + "${overrides_args[@]}" \ | tee ./diff-report.md rc=$? diff --git a/.github/workflows/db-nightly.yml b/.github/workflows/db-nightly.yml index dfdac76..78ec007 100644 --- a/.github/workflows/db-nightly.yml +++ b/.github/workflows/db-nightly.yml @@ -6,13 +6,35 @@ on: workflow_dispatch: inputs: db_change_rate_threshold: - description: "`vuls diff db` change rate (%) threshold per ecosystem; diff fails if exceeded" + description: "`vuls diff db` default change rate (%) threshold per ecosystem; diff fails if exceeded" required: false default: "10" + db_change_rate_threshold_overrides: + description: | + Per-ecosystem overrides of `vuls diff db` threshold. One + "=" entry per line (e.g. "ubuntu:26.04=25"); + blank lines and trailing "# ..." comments are stripped at parse time. + Persistent overrides should be added to this default in a PR (with + rationale in the PR description) rather than at dispatch. + required: false + type: string + # Persistent override entries live in this default. vuls2 logs a + # warning when an override key matches no ecosystem, so stale entries + # surface in run logs and should be removed in follow-up PRs. + default: "" detection_change_rate_threshold: - description: "`vuls diff detection` change rate (%) threshold per scan result; diff fails if exceeded" + description: "`vuls diff detection` default change rate (%) threshold per scan result; diff fails if exceeded" required: false default: "5" + detection_change_rate_threshold_overrides: + description: | + Per-scan-result-file overrides of `vuls diff detection` threshold. + One "=" entry per line (e.g. "debian_13=8"); + blank lines and trailing "# ..." comments are stripped at parse time. + Persistent overrides should be added to this default in a PR. + required: false + type: string + default: "" permissions: contents: read @@ -88,12 +110,17 @@ jobs: # On scheduled runs, github.event.inputs.* is typically unset/null # rather than an empty string. The `|| 'default'` fallbacks below # let this workflow run without workflow_dispatch inputs. + # The `_overrides` inputs default to an empty string when unset so + # the composite action's whitespace/comment-only handling treats them + # as "no overrides". uses: ./.github/actions/diff-guard with: target_db: ./vuls.db baseline_repository: ghcr.io/vulsio/vuls-nightly-db:nightly db_change_rate_threshold: ${{ github.event.inputs.db_change_rate_threshold || '10' }} + db_change_rate_threshold_overrides: ${{ github.event.inputs.db_change_rate_threshold_overrides || '' }} detection_change_rate_threshold: ${{ github.event.inputs.detection_change_rate_threshold || '5' }} + detection_change_rate_threshold_overrides: ${{ github.event.inputs.detection_change_rate_threshold_overrides || '' }} # Guard passed: attach :nightly to the verified digest. If the guard # fails this step is skipped and From 099d9ce750fb2c34762285a982fc3fa63ef80c9e Mon Sep 17 00:00:00 2001 From: Shunichi Shinohara Date: Fri, 8 May 2026 12:45:19 +0900 Subject: [PATCH 02/10] refactor(ci/diff-guard): centralize override parsing into a helper script The override-expansion loop was duplicated across the three diff steps (detection master, detection old, db). Two issues with that: 1. The inline loop stripped trailing "# ..." comments but did not trim leading or trailing whitespace from the remaining entry, so an indented or trailing-space line could produce a key with a literal space attached, making the override silently miss its target. 2. Three copies of the same shell logic invite drift if the parsing rules change. Move the parsing into ./expand-overrides.sh and have each diff step mapfile-read its output into the override-args array. The helper strips comments, normalizes whitespace via `awk '{$1=$1};1'`, skips blank entries, and emits one `--change-rate-threshold-override ` token pair per surviving line. Empty / unset input yields no tokens, so an array expansion with zero elements still produces the same command line as before for callers that don't supply overrides. Reported by copilot-pull-request-reviewer on #146. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/actions/diff-guard/action.yml | 41 ++++++++----------- .../actions/diff-guard/expand-overrides.sh | 40 ++++++++++++++++++ 2 files changed, 57 insertions(+), 24 deletions(-) create mode 100755 .github/actions/diff-guard/expand-overrides.sh diff --git a/.github/actions/diff-guard/action.yml b/.github/actions/diff-guard/action.yml index 1470e2c..9e06f9d 100644 --- a/.github/actions/diff-guard/action.yml +++ b/.github/actions/diff-guard/action.yml @@ -177,16 +177,13 @@ runs: set +e set -o pipefail - # Expand the multi-line "=" input into a - # repeated `--change-rate-threshold-override =` flag. - # Blank lines and trailing "#" comments are skipped here; value - # validation (numeric, key non-empty, etc.) happens in vuls2. - overrides_args=() - while IFS= read -r line; do - line="${line%%#*}" - [[ "$line" =~ ^[[:space:]]*$ ]] && continue - overrides_args+=(--change-rate-threshold-override "$line") - done <<< "$DETECTION_CHANGE_RATE_THRESHOLD_OVERRIDES" + # Centralized expansion of multi-line "=" overrides into + # repeated `--change-rate-threshold-override ` flags. See + # ./expand-overrides.sh for the parsing rules. + mapfile -t overrides_args < <( + bash "${GITHUB_ACTION_PATH}/expand-overrides.sh" \ + DETECTION_CHANGE_RATE_THRESHOLD_OVERRIDES + ) vuls diff detection \ ./scan-results \ @@ -262,13 +259,11 @@ runs: set +e set -o pipefail - # See diff_detection_master for the override-array build rationale. - overrides_args=() - while IFS= read -r line; do - line="${line%%#*}" - [[ "$line" =~ ^[[:space:]]*$ ]] && continue - overrides_args+=(--change-rate-threshold-override "$line") - done <<< "$DETECTION_CHANGE_RATE_THRESHOLD_OVERRIDES" + # See diff_detection_master and ./expand-overrides.sh for parsing rules. + mapfile -t overrides_args < <( + bash "${GITHUB_ACTION_PATH}/expand-overrides.sh" \ + DETECTION_CHANGE_RATE_THRESHOLD_OVERRIDES + ) vuls diff detection \ ./scan-results-old \ @@ -303,13 +298,11 @@ runs: set +e set -o pipefail - # See diff_detection_master for the override-array build rationale. - overrides_args=() - while IFS= read -r line; do - line="${line%%#*}" - [[ "$line" =~ ^[[:space:]]*$ ]] && continue - overrides_args+=(--change-rate-threshold-override "$line") - done <<< "$DB_CHANGE_RATE_THRESHOLD_OVERRIDES" + # See diff_detection_master and ./expand-overrides.sh for parsing rules. + mapfile -t overrides_args < <( + bash "${GITHUB_ACTION_PATH}/expand-overrides.sh" \ + DB_CHANGE_RATE_THRESHOLD_OVERRIDES + ) vuls diff db ./baseline.db "$TARGET_DB" \ --change-rate-threshold "$DB_CHANGE_RATE_THRESHOLD" \ diff --git a/.github/actions/diff-guard/expand-overrides.sh b/.github/actions/diff-guard/expand-overrides.sh new file mode 100755 index 0000000..86d72ee --- /dev/null +++ b/.github/actions/diff-guard/expand-overrides.sh @@ -0,0 +1,40 @@ +#!/usr/bin/env bash +# expand-overrides.sh — Expand a multi-line "=" string (read from +# the env var named by $1) into a stream of `--change-rate-threshold-override +# ` tokens, one per line, on stdout. +# +# Caller pattern (each diff-guard step): +# +# mapfile -t overrides_args < <( +# bash "${GITHUB_ACTION_PATH}/expand-overrides.sh" \ +# DETECTION_CHANGE_RATE_THRESHOLD_OVERRIDES +# ) +# vuls diff detection ... "${overrides_args[@]}" +# +# Why centralize: the parsing rules (comment stripping, whitespace handling, +# blank-line skipping) used to live inline in three diff steps. Keeping them +# in one place avoids drift if those rules ever change. Value-shape +# validation (numeric, non-empty key, non-negative, finite) intentionally +# stays in vuls2's CLI parser — this script is purely a textual splitter. +# +# Usage: expand-overrides.sh +set -euo pipefail + +if [ "$#" -ne 1 ]; then + echo "expand-overrides.sh: expected exactly 1 arg (env var name), got $#" >&2 + exit 2 +fi + +input="${!1-}" + +while IFS= read -r line; do + # Strip trailing "# ..." comments. Note: this is a deliberate one-pass + # strip — there's no syntax for escaping a literal "#" in entries because + # ecosystem ids and scan-result file basenames don't contain "#". + line="${line%%#*}" + # Trim leading and trailing whitespace (spaces and tabs). awk's "{$1=$1};1" + # is the standard idiom for whitespace normalization. + line="$(printf '%s' "$line" | awk '{$1=$1};1')" + [ -z "$line" ] && continue + printf -- '--change-rate-threshold-override\n%s\n' "$line" +done <<< "$input" From b10f26caaac2929ba2f3a996993104cf17c23bfe Mon Sep 17 00:00:00 2001 From: Shunichi Shinohara Date: Wed, 13 May 2026 19:01:51 +0900 Subject: [PATCH 03/10] refactor(ci/diff-guard): inline override expansion, drop the helper script MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `expand-overrides.sh` existed to strip `# ...` inline comments and normalize whitespace before emitting paired `--change-rate-threshold-override ` tokens for `mapfile` to slurp into an array. In the actual end-to-end pipeline the script was doing too much: vuls2's `override.Parse` already calls `strings.TrimSpace` around the `=`, and inline `# ...` comments inside the workflow input string are an unsupported quirk that produced parse-time errors anyway. Drop the script and the inline-comment-stripping promise it made, then collapse the expansion to three bash lines per step: overrides_csv=$(printf '%s' "$INPUT" | awk 'NF' | paste -sd, -) overrides_args=() [ -n "$overrides_csv" ] && overrides_args+=(--change-rate-threshold-override "$overrides_csv") `awk 'NF'` drops blank/whitespace-only lines (NF = 0 for those under awk's default field separator); `paste -sd, -` joins survivors with ",". cobra's `StringSliceVar` already splits on commas, so passing a single comma-joined value is exactly equivalent to passing the flag N times — and an empty input naturally produces zero args, matching the no-overrides default exactly. Updated the input descriptions on both action.yml and db-nightly.yml to make the new contract explicit: blank lines dropped, but inline `# ...` comments NOT supported (rationale belongs in PR descriptions or YAML comments outside the default string). Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/actions/diff-guard/action.yml | 50 ++++++++++--------- .../actions/diff-guard/expand-overrides.sh | 40 --------------- .github/workflows/db-nightly.yml | 20 ++++---- 3 files changed, 37 insertions(+), 73 deletions(-) delete mode 100755 .github/actions/diff-guard/expand-overrides.sh diff --git a/.github/actions/diff-guard/action.yml b/.github/actions/diff-guard/action.yml index 9e06f9d..77ae29a 100644 --- a/.github/actions/diff-guard/action.yml +++ b/.github/actions/diff-guard/action.yml @@ -71,9 +71,11 @@ inputs: db_change_rate_threshold_overrides: description: | Per-ecosystem overrides of the `vuls diff db` change rate threshold. - Multi-line "=" entries (e.g. "ubuntu:26.04=25"); blank - lines and trailing "#" comments are ignored. Empty value means "no - overrides" — every ecosystem uses db_change_rate_threshold. + One "=" entry per line (e.g. "ubuntu:26.04=25"); + blank/whitespace-only lines are dropped, but trailing "# ..." inline + comments are NOT stripped (would be parsed as part of the value and + rejected by vuls2). Empty value means "no overrides" — every + ecosystem uses db_change_rate_threshold. required: false default: "" detection_change_rate_threshold: @@ -82,9 +84,10 @@ inputs: detection_change_rate_threshold_overrides: description: | Per-file overrides of the `vuls diff detection` change rate threshold. - Multi-line "=" entries (e.g. "debian_13=8") where - the basename is the scan-result file name without ".json"; blank lines - and trailing "#" comments are ignored. + One "=" entry per line (e.g. "debian_13=8") where + the basename is the scan-result file name without ".json". + Blank/whitespace-only lines are dropped; trailing "# ..." inline + comments are NOT stripped (would be rejected by vuls2). required: false default: "" integration_ref: @@ -177,13 +180,16 @@ runs: set +e set -o pipefail - # Centralized expansion of multi-line "=" overrides into - # repeated `--change-rate-threshold-override ` flags. See - # ./expand-overrides.sh for the parsing rules. - mapfile -t overrides_args < <( - bash "${GITHUB_ACTION_PATH}/expand-overrides.sh" \ - DETECTION_CHANGE_RATE_THRESHOLD_OVERRIDES - ) + # Collapse the multi-line "=" input into a single + # comma-separated string. `awk 'NF'` drops blank/whitespace-only + # lines (NF is 0 for them); `paste -sd, -` joins survivors with + # ",". Whitespace around each entry is intentionally NOT trimmed + # here — vuls2 trims around the "=" itself in override.Parse. + # Empty input yields an empty $overrides_csv → the flag is + # omitted, matching the no-overrides default exactly. + overrides_csv=$(printf '%s' "$DETECTION_CHANGE_RATE_THRESHOLD_OVERRIDES" | awk 'NF' | paste -sd, -) + overrides_args=() + [ -n "$overrides_csv" ] && overrides_args+=(--change-rate-threshold-override "$overrides_csv") vuls diff detection \ ./scan-results \ @@ -259,11 +265,10 @@ runs: set +e set -o pipefail - # See diff_detection_master and ./expand-overrides.sh for parsing rules. - mapfile -t overrides_args < <( - bash "${GITHUB_ACTION_PATH}/expand-overrides.sh" \ - DETECTION_CHANGE_RATE_THRESHOLD_OVERRIDES - ) + # See diff_detection_master for the parsing-rule rationale. + overrides_csv=$(printf '%s' "$DETECTION_CHANGE_RATE_THRESHOLD_OVERRIDES" | awk 'NF' | paste -sd, -) + overrides_args=() + [ -n "$overrides_csv" ] && overrides_args+=(--change-rate-threshold-override "$overrides_csv") vuls diff detection \ ./scan-results-old \ @@ -298,11 +303,10 @@ runs: set +e set -o pipefail - # See diff_detection_master and ./expand-overrides.sh for parsing rules. - mapfile -t overrides_args < <( - bash "${GITHUB_ACTION_PATH}/expand-overrides.sh" \ - DB_CHANGE_RATE_THRESHOLD_OVERRIDES - ) + # See diff_detection_master for the parsing-rule rationale. + overrides_csv=$(printf '%s' "$DB_CHANGE_RATE_THRESHOLD_OVERRIDES" | awk 'NF' | paste -sd, -) + overrides_args=() + [ -n "$overrides_csv" ] && overrides_args+=(--change-rate-threshold-override "$overrides_csv") vuls diff db ./baseline.db "$TARGET_DB" \ --change-rate-threshold "$DB_CHANGE_RATE_THRESHOLD" \ diff --git a/.github/actions/diff-guard/expand-overrides.sh b/.github/actions/diff-guard/expand-overrides.sh deleted file mode 100755 index 86d72ee..0000000 --- a/.github/actions/diff-guard/expand-overrides.sh +++ /dev/null @@ -1,40 +0,0 @@ -#!/usr/bin/env bash -# expand-overrides.sh — Expand a multi-line "=" string (read from -# the env var named by $1) into a stream of `--change-rate-threshold-override -# ` tokens, one per line, on stdout. -# -# Caller pattern (each diff-guard step): -# -# mapfile -t overrides_args < <( -# bash "${GITHUB_ACTION_PATH}/expand-overrides.sh" \ -# DETECTION_CHANGE_RATE_THRESHOLD_OVERRIDES -# ) -# vuls diff detection ... "${overrides_args[@]}" -# -# Why centralize: the parsing rules (comment stripping, whitespace handling, -# blank-line skipping) used to live inline in three diff steps. Keeping them -# in one place avoids drift if those rules ever change. Value-shape -# validation (numeric, non-empty key, non-negative, finite) intentionally -# stays in vuls2's CLI parser — this script is purely a textual splitter. -# -# Usage: expand-overrides.sh -set -euo pipefail - -if [ "$#" -ne 1 ]; then - echo "expand-overrides.sh: expected exactly 1 arg (env var name), got $#" >&2 - exit 2 -fi - -input="${!1-}" - -while IFS= read -r line; do - # Strip trailing "# ..." comments. Note: this is a deliberate one-pass - # strip — there's no syntax for escaping a literal "#" in entries because - # ecosystem ids and scan-result file basenames don't contain "#". - line="${line%%#*}" - # Trim leading and trailing whitespace (spaces and tabs). awk's "{$1=$1};1" - # is the standard idiom for whitespace normalization. - line="$(printf '%s' "$line" | awk '{$1=$1};1')" - [ -z "$line" ] && continue - printf -- '--change-rate-threshold-override\n%s\n' "$line" -done <<< "$input" diff --git a/.github/workflows/db-nightly.yml b/.github/workflows/db-nightly.yml index 78ec007..3258190 100644 --- a/.github/workflows/db-nightly.yml +++ b/.github/workflows/db-nightly.yml @@ -12,15 +12,14 @@ on: db_change_rate_threshold_overrides: description: | Per-ecosystem overrides of `vuls diff db` threshold. One - "=" entry per line (e.g. "ubuntu:26.04=25"); - blank lines and trailing "# ..." comments are stripped at parse time. - Persistent overrides should be added to this default in a PR (with - rationale in the PR description) rather than at dispatch. + "=" entry per line (e.g. "ubuntu:26.04=25"). + Blank/whitespace-only lines are dropped; inline "# ..." comments + are NOT supported (would be rejected by vuls2). Persistent + overrides should be added to this default in a PR (with rationale + in the PR description / YAML comments outside the default string) + rather than at dispatch. required: false type: string - # Persistent override entries live in this default. vuls2 logs a - # warning when an override key matches no ecosystem, so stale entries - # surface in run logs and should be removed in follow-up PRs. default: "" detection_change_rate_threshold: description: "`vuls diff detection` default change rate (%) threshold per scan result; diff fails if exceeded" @@ -29,9 +28,10 @@ on: detection_change_rate_threshold_overrides: description: | Per-scan-result-file overrides of `vuls diff detection` threshold. - One "=" entry per line (e.g. "debian_13=8"); - blank lines and trailing "# ..." comments are stripped at parse time. - Persistent overrides should be added to this default in a PR. + One "=" entry per line (e.g. "debian_13=8"). + Blank/whitespace-only lines are dropped; inline "# ..." comments + are NOT supported. Persistent overrides should be added to this + default in a PR. required: false type: string default: "" From 5abce22a1b42d27bb2166f10fe9172d16d099b05 Mon Sep 17 00:00:00 2001 From: Shunichi Shinohara Date: Tue, 19 May 2026 10:36:55 +0900 Subject: [PATCH 04/10] docs(ci/db-nightly): correct misleading "comment-only handling" note MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The `Run diff guard` step's comment claimed the composite action has "whitespace/comment-only handling" for the `_overrides` inputs, but the action only drops blank/whitespace-only lines — it does not strip inline `# ...` comments. Rewrite the comment to describe the actual behavior (drop blanks, omit the flag when the result is empty) so operators reading the workflow are not misled. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/db-nightly.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/db-nightly.yml b/.github/workflows/db-nightly.yml index 3258190..c152f7c 100644 --- a/.github/workflows/db-nightly.yml +++ b/.github/workflows/db-nightly.yml @@ -110,9 +110,10 @@ jobs: # On scheduled runs, github.event.inputs.* is typically unset/null # rather than an empty string. The `|| 'default'` fallbacks below # let this workflow run without workflow_dispatch inputs. - # The `_overrides` inputs default to an empty string when unset so - # the composite action's whitespace/comment-only handling treats them - # as "no overrides". + # The `_overrides` inputs default to an empty string when unset; + # the composite action drops blank/whitespace-only lines and treats + # an empty result as "no overrides" (the `--change-rate-threshold-override` + # flag is omitted entirely). uses: ./.github/actions/diff-guard with: target_db: ./vuls.db From 15ef023100186a4064d54f5ccbbf9eaacdcf1e7e Mon Sep 17 00:00:00 2001 From: Shunichi Shinohara Date: Tue, 19 May 2026 10:45:36 +0900 Subject: [PATCH 05/10] refactor(ci/diff-guard): centralize _overrides parsing into one step Move the multi-line `_overrides` input parsing out of the three diff steps (diff_detection_master, diff_detection_old, diff_db) into a single `Parse threshold overrides` step that emits `detection_csv` and `db_csv` as step outputs. Each diff step now reads the relevant output via `steps.parse_overrides.outputs._csv` and only retains the local flag-omission guard (`[ -n "$OVERRIDES_CSV" ] && ...`), which is genuinely per-step (the consuming command differs). Eliminates the drift risk Copilot flagged: a future change to the parsing rule (whitespace handling, trimming, validation) lives in one place instead of being threaded through three near-identical bash blocks. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/actions/diff-guard/action.yml | 52 +++++++++++++++++---------- 1 file changed, 34 insertions(+), 18 deletions(-) diff --git a/.github/actions/diff-guard/action.yml b/.github/actions/diff-guard/action.yml index 77ae29a..53e8b9d 100644 --- a/.github/actions/diff-guard/action.yml +++ b/.github/actions/diff-guard/action.yml @@ -162,6 +162,27 @@ runs: echo "## Scan-result fixtures source" >> "$GITHUB_STEP_SUMMARY" echo "- vulsio/integration @ \`${integration_sha}\`" >> "$GITHUB_STEP_SUMMARY" + # Centralizes the multi-line `_overrides` input parsing for all three + # diff steps below. Rule: `awk 'NF'` drops blank/whitespace-only lines + # (NF is 0 for them); `paste -sd, -` joins survivors with ",". + # Whitespace around each entry is intentionally NOT trimmed here — + # vuls2 trims around the "=" itself in override.Parse. Inline "# ..." + # comments are also NOT stripped (would be forwarded as part of the + # value and rejected by vuls2). Empty result → consuming diff step + # omits the `--change-rate-threshold-override` flag entirely, matching + # the no-overrides default exactly. + - name: Parse threshold overrides + id: parse_overrides + shell: bash + env: + DETECTION_OVERRIDES: ${{ inputs.detection_change_rate_threshold_overrides }} + DB_OVERRIDES: ${{ inputs.db_change_rate_threshold_overrides }} + run: | + detection_csv=$(printf '%s' "$DETECTION_OVERRIDES" | awk 'NF' | paste -sd, -) + db_csv=$(printf '%s' "$DB_OVERRIDES" | awk 'NF' | paste -sd, -) + echo "detection_csv=${detection_csv}" >> "$GITHUB_OUTPUT" + echo "db_csv=${db_csv}" >> "$GITHUB_OUTPUT" + - name: Diff detection between baseline and target DBs id: diff_detection_master # See the `Failure-handling strategy` section in the action's @@ -172,7 +193,7 @@ runs: env: TARGET_DB: ${{ inputs.target_db }} DETECTION_CHANGE_RATE_THRESHOLD: ${{ inputs.detection_change_rate_threshold }} - DETECTION_CHANGE_RATE_THRESHOLD_OVERRIDES: ${{ inputs.detection_change_rate_threshold_overrides }} + OVERRIDES_CSV: ${{ steps.parse_overrides.outputs.detection_csv }} run: | # No `set -e` after `rc=$?` — the rest of the step must reach # the final `echo "rc=..." >> $GITHUB_OUTPUT` so the aggregator @@ -180,16 +201,11 @@ runs: set +e set -o pipefail - # Collapse the multi-line "=" input into a single - # comma-separated string. `awk 'NF'` drops blank/whitespace-only - # lines (NF is 0 for them); `paste -sd, -` joins survivors with - # ",". Whitespace around each entry is intentionally NOT trimmed - # here — vuls2 trims around the "=" itself in override.Parse. - # Empty input yields an empty $overrides_csv → the flag is - # omitted, matching the no-overrides default exactly. - overrides_csv=$(printf '%s' "$DETECTION_CHANGE_RATE_THRESHOLD_OVERRIDES" | awk 'NF' | paste -sd, -) + # `$OVERRIDES_CSV` comes from the `Parse threshold overrides` + # step (single source of truth for the parsing rule). Empty → + # flag is omitted, matching the no-overrides default exactly. overrides_args=() - [ -n "$overrides_csv" ] && overrides_args+=(--change-rate-threshold-override "$overrides_csv") + [ -n "$OVERRIDES_CSV" ] && overrides_args+=(--change-rate-threshold-override "$OVERRIDES_CSV") vuls diff detection \ ./scan-results \ @@ -258,17 +274,17 @@ runs: env: TARGET_DB: ${{ inputs.target_db }} DETECTION_CHANGE_RATE_THRESHOLD: ${{ inputs.detection_change_rate_threshold }} - DETECTION_CHANGE_RATE_THRESHOLD_OVERRIDES: ${{ inputs.detection_change_rate_threshold_overrides }} + OVERRIDES_CSV: ${{ steps.parse_overrides.outputs.detection_csv }} VULS0_OLD_REF: ${{ inputs.vuls0_old_ref }} run: | # Stay in `set +e` for the entire step. See note above. set +e set -o pipefail - # See diff_detection_master for the parsing-rule rationale. - overrides_csv=$(printf '%s' "$DETECTION_CHANGE_RATE_THRESHOLD_OVERRIDES" | awk 'NF' | paste -sd, -) + # `$OVERRIDES_CSV` comes from the `Parse threshold overrides` + # step. See diff_detection_master for the flag-omission rationale. overrides_args=() - [ -n "$overrides_csv" ] && overrides_args+=(--change-rate-threshold-override "$overrides_csv") + [ -n "$OVERRIDES_CSV" ] && overrides_args+=(--change-rate-threshold-override "$OVERRIDES_CSV") vuls diff detection \ ./scan-results-old \ @@ -297,16 +313,16 @@ runs: env: TARGET_DB: ${{ inputs.target_db }} DB_CHANGE_RATE_THRESHOLD: ${{ inputs.db_change_rate_threshold }} - DB_CHANGE_RATE_THRESHOLD_OVERRIDES: ${{ inputs.db_change_rate_threshold_overrides }} + OVERRIDES_CSV: ${{ steps.parse_overrides.outputs.db_csv }} run: | # Stay in `set +e` for the entire step. See note above. set +e set -o pipefail - # See diff_detection_master for the parsing-rule rationale. - overrides_csv=$(printf '%s' "$DB_CHANGE_RATE_THRESHOLD_OVERRIDES" | awk 'NF' | paste -sd, -) + # `$OVERRIDES_CSV` comes from the `Parse threshold overrides` + # step. See diff_detection_master for the flag-omission rationale. overrides_args=() - [ -n "$overrides_csv" ] && overrides_args+=(--change-rate-threshold-override "$overrides_csv") + [ -n "$OVERRIDES_CSV" ] && overrides_args+=(--change-rate-threshold-override "$OVERRIDES_CSV") vuls diff db ./baseline.db "$TARGET_DB" \ --change-rate-threshold "$DB_CHANGE_RATE_THRESHOLD" \ From bf1a65c20476a552b30a618f7c3a0324c6159944 Mon Sep 17 00:00:00 2001 From: Shunichi Shinohara Date: Wed, 20 May 2026 11:11:23 +0900 Subject: [PATCH 06/10] docs(ci/diff-guard): drop the inline-"#"-comment caveat from input docs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The "inline # ... comments are NOT stripped / rejected by vuls2" note was a leftover from a mid-PR iteration: an earlier revision had a helper script that stripped # comments, and when that was removed the docs gained a caveat explaining the now-absent feature. To a reader who didn't follow that history the caveat just raises the question "wait, why are we talking about # comments at all?" — the override format never had comment support in the shipped design. Remove the caveat from both `_overrides` input descriptions in action.yml, the matching descriptions in db-nightly.yml, and the `Parse threshold overrides` step comment. The parsing rule that remains (drop blank/whitespace-only lines, comma-join the rest) is described without reference to the abandoned feature. Docs-only; no behavior change. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/actions/diff-guard/action.yml | 17 ++++++----------- .github/workflows/db-nightly.yml | 13 +++++-------- 2 files changed, 11 insertions(+), 19 deletions(-) diff --git a/.github/actions/diff-guard/action.yml b/.github/actions/diff-guard/action.yml index 53e8b9d..84760a5 100644 --- a/.github/actions/diff-guard/action.yml +++ b/.github/actions/diff-guard/action.yml @@ -72,10 +72,8 @@ inputs: description: | Per-ecosystem overrides of the `vuls diff db` change rate threshold. One "=" entry per line (e.g. "ubuntu:26.04=25"); - blank/whitespace-only lines are dropped, but trailing "# ..." inline - comments are NOT stripped (would be parsed as part of the value and - rejected by vuls2). Empty value means "no overrides" — every - ecosystem uses db_change_rate_threshold. + blank/whitespace-only lines are dropped. Empty value means "no + overrides" — every ecosystem uses db_change_rate_threshold. required: false default: "" detection_change_rate_threshold: @@ -86,8 +84,7 @@ inputs: Per-file overrides of the `vuls diff detection` change rate threshold. One "=" entry per line (e.g. "debian_13=8") where the basename is the scan-result file name without ".json". - Blank/whitespace-only lines are dropped; trailing "# ..." inline - comments are NOT stripped (would be rejected by vuls2). + Blank/whitespace-only lines are dropped. required: false default: "" integration_ref: @@ -166,11 +163,9 @@ runs: # diff steps below. Rule: `awk 'NF'` drops blank/whitespace-only lines # (NF is 0 for them); `paste -sd, -` joins survivors with ",". # Whitespace around each entry is intentionally NOT trimmed here — - # vuls2 trims around the "=" itself in override.Parse. Inline "# ..." - # comments are also NOT stripped (would be forwarded as part of the - # value and rejected by vuls2). Empty result → consuming diff step - # omits the `--change-rate-threshold-override` flag entirely, matching - # the no-overrides default exactly. + # vuls2 trims around the "=" itself in override.Parse. Empty result → + # consuming diff step omits the `--change-rate-threshold-override` flag + # entirely, matching the no-overrides default exactly. - name: Parse threshold overrides id: parse_overrides shell: bash diff --git a/.github/workflows/db-nightly.yml b/.github/workflows/db-nightly.yml index c152f7c..0bec895 100644 --- a/.github/workflows/db-nightly.yml +++ b/.github/workflows/db-nightly.yml @@ -13,11 +13,9 @@ on: description: | Per-ecosystem overrides of `vuls diff db` threshold. One "=" entry per line (e.g. "ubuntu:26.04=25"). - Blank/whitespace-only lines are dropped; inline "# ..." comments - are NOT supported (would be rejected by vuls2). Persistent - overrides should be added to this default in a PR (with rationale - in the PR description / YAML comments outside the default string) - rather than at dispatch. + Blank/whitespace-only lines are dropped. Persistent overrides + should be added to this default in a PR (with rationale in the + PR description) rather than at dispatch. required: false type: string default: "" @@ -29,9 +27,8 @@ on: description: | Per-scan-result-file overrides of `vuls diff detection` threshold. One "=" entry per line (e.g. "debian_13=8"). - Blank/whitespace-only lines are dropped; inline "# ..." comments - are NOT supported. Persistent overrides should be added to this - default in a PR. + Blank/whitespace-only lines are dropped. Persistent overrides + should be added to this default in a PR. required: false type: string default: "" From c1ebf3026dfcdcffc2e037673d0640fc4ad94836 Mon Sep 17 00:00:00 2001 From: Shunichi Shinohara Date: Wed, 20 May 2026 11:37:25 +0900 Subject: [PATCH 07/10] refactor(ci/diff-guard): printf for step outputs, pass override flag verbatim MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two review comments on #146: 1. The `Parse threshold overrides` step wrote its outputs with `echo`. `echo` has implementation-defined handling of a leading `-` (a value like `-n` is consumed as a flag) and of backslashes. Switch both `$GITHUB_OUTPUT` writes to `printf '%s\n'`, which emits the bytes verbatim. 2. The `overrides_args` array construction (`overrides_args=(); [ -n ... ] && overrides_args+=(...)`) was duplicated across all three diff steps. vuls2's `--change-rate-threshold-override` is a cobra StringSlice flag, and pflag parses an empty value into an empty slice — so passing the flag with an empty value is exactly equivalent to omitting it. Each diff step now just appends `--change-rate-threshold-override "$OVERRIDES_CSV"` unconditionally; the array juggling is gone from all three. This also removes a latent `overrides_argsa=()` typo in the older-binary step (the misnamed array happened to be harmless because the real `overrides_args` was created by the `+=`, but the line is deleted entirely now). No behavior change: empty overrides still yield an identical diff result. Docs (action.yml comments, db-nightly.yml comment, PR body) updated to describe the verbatim-pass model. Reported by copilot-pull-request-reviewer on #146. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/actions/diff-guard/action.yml | 37 ++++++++++++--------------- .github/workflows/db-nightly.yml | 5 ++-- 2 files changed, 18 insertions(+), 24 deletions(-) diff --git a/.github/actions/diff-guard/action.yml b/.github/actions/diff-guard/action.yml index 84760a5..b49a196 100644 --- a/.github/actions/diff-guard/action.yml +++ b/.github/actions/diff-guard/action.yml @@ -163,9 +163,9 @@ runs: # diff steps below. Rule: `awk 'NF'` drops blank/whitespace-only lines # (NF is 0 for them); `paste -sd, -` joins survivors with ",". # Whitespace around each entry is intentionally NOT trimmed here — - # vuls2 trims around the "=" itself in override.Parse. Empty result → - # consuming diff step omits the `--change-rate-threshold-override` flag - # entirely, matching the no-overrides default exactly. + # vuls2 trims around the "=" itself in override.Parse. An empty result + # is forwarded as-is: vuls2's StringSlice flag parses "" into an empty + # override list, i.e. the no-overrides default. - name: Parse threshold overrides id: parse_overrides shell: bash @@ -175,8 +175,11 @@ runs: run: | detection_csv=$(printf '%s' "$DETECTION_OVERRIDES" | awk 'NF' | paste -sd, -) db_csv=$(printf '%s' "$DB_OVERRIDES" | awk 'NF' | paste -sd, -) - echo "detection_csv=${detection_csv}" >> "$GITHUB_OUTPUT" - echo "db_csv=${db_csv}" >> "$GITHUB_OUTPUT" + # printf, not echo: a value beginning with `-` (e.g. `-n`) or + # containing backslashes is written verbatim, avoiding echo's + # implementation-defined option/escape handling. + printf '%s\n' "detection_csv=${detection_csv}" >> "$GITHUB_OUTPUT" + printf '%s\n' "db_csv=${db_csv}" >> "$GITHUB_OUTPUT" - name: Diff detection between baseline and target DBs id: diff_detection_master @@ -197,17 +200,15 @@ runs: set -o pipefail # `$OVERRIDES_CSV` comes from the `Parse threshold overrides` - # step (single source of truth for the parsing rule). Empty → - # flag is omitted, matching the no-overrides default exactly. - overrides_args=() - [ -n "$OVERRIDES_CSV" ] && overrides_args+=(--change-rate-threshold-override "$OVERRIDES_CSV") - + # step. Passed verbatim: vuls2's StringSlice flag parses an empty + # value into an empty override list, so the no-overrides path + # needs no special-casing here. vuls diff detection \ ./scan-results \ ./baseline.db "$VULS0" \ "$TARGET_DB" "$VULS0" \ --change-rate-threshold "$DETECTION_CHANGE_RATE_THRESHOLD" \ - "${overrides_args[@]}" \ + --change-rate-threshold-override "$OVERRIDES_CSV" \ | tee ./diff-detection-report.md rc=$? @@ -277,16 +278,13 @@ runs: set -o pipefail # `$OVERRIDES_CSV` comes from the `Parse threshold overrides` - # step. See diff_detection_master for the flag-omission rationale. - overrides_args=() - [ -n "$OVERRIDES_CSV" ] && overrides_args+=(--change-rate-threshold-override "$OVERRIDES_CSV") - + # step. See diff_detection_master for the verbatim-pass rationale. vuls diff detection \ ./scan-results-old \ ./baseline.db "$VULS0_OLD" \ "$TARGET_DB" "$VULS0_OLD" \ --change-rate-threshold "$DETECTION_CHANGE_RATE_THRESHOLD" \ - "${overrides_args[@]}" \ + --change-rate-threshold-override "$OVERRIDES_CSV" \ | tee ./diff-detection-old-report.md rc=$? @@ -315,13 +313,10 @@ runs: set -o pipefail # `$OVERRIDES_CSV` comes from the `Parse threshold overrides` - # step. See diff_detection_master for the flag-omission rationale. - overrides_args=() - [ -n "$OVERRIDES_CSV" ] && overrides_args+=(--change-rate-threshold-override "$OVERRIDES_CSV") - + # step. See diff_detection_master for the verbatim-pass rationale. vuls diff db ./baseline.db "$TARGET_DB" \ --change-rate-threshold "$DB_CHANGE_RATE_THRESHOLD" \ - "${overrides_args[@]}" \ + --change-rate-threshold-override "$OVERRIDES_CSV" \ | tee ./diff-report.md rc=$? diff --git a/.github/workflows/db-nightly.yml b/.github/workflows/db-nightly.yml index 0bec895..8db8c28 100644 --- a/.github/workflows/db-nightly.yml +++ b/.github/workflows/db-nightly.yml @@ -108,9 +108,8 @@ jobs: # rather than an empty string. The `|| 'default'` fallbacks below # let this workflow run without workflow_dispatch inputs. # The `_overrides` inputs default to an empty string when unset; - # the composite action drops blank/whitespace-only lines and treats - # an empty result as "no overrides" (the `--change-rate-threshold-override` - # flag is omitted entirely). + # the composite action drops blank/whitespace-only lines and an + # empty result is parsed by vuls2 as "no overrides". uses: ./.github/actions/diff-guard with: target_db: ./vuls.db From dd924a6203941c741ad8b697d59ba62480636cc2 Mon Sep 17 00:00:00 2001 From: Shunichi Shinohara Date: Wed, 20 May 2026 12:08:25 +0900 Subject: [PATCH 08/10] docs(ci/db-nightly): note that entry whitespace is trimmed by vuls2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The `_overrides` input descriptions said blank/whitespace-only lines are dropped, which left it ambiguous whether leading/trailing whitespace around a non-empty entry's key/value matters. It does not: the composite action deliberately does not trim per-entry whitespace — vuls2's override parser trims both sides of the key and the value, so ` ubuntu:26.04 = 25 ` resolves identically to `ubuntu:26.04=25`. Spell that out in both `_overrides` descriptions so an operator does not assume a stray space silently no-ops an override. Reported by copilot-pull-request-reviewer on #146. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/db-nightly.yml | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/.github/workflows/db-nightly.yml b/.github/workflows/db-nightly.yml index 8db8c28..9c0ec8c 100644 --- a/.github/workflows/db-nightly.yml +++ b/.github/workflows/db-nightly.yml @@ -13,9 +13,12 @@ on: description: | Per-ecosystem overrides of `vuls diff db` threshold. One "=" entry per line (e.g. "ubuntu:26.04=25"). - Blank/whitespace-only lines are dropped. Persistent overrides - should be added to this default in a PR (with rationale in the - PR description) rather than at dispatch. + Blank/whitespace-only lines are dropped. Leading/trailing + whitespace around an entry's key and value is not trimmed + here — vuls2's override parser trims both sides, so it has no + effect on the resolved override. Persistent overrides should + be added to this default in a PR (with rationale in the PR + description) rather than at dispatch. required: false type: string default: "" @@ -27,8 +30,11 @@ on: description: | Per-scan-result-file overrides of `vuls diff detection` threshold. One "=" entry per line (e.g. "debian_13=8"). - Blank/whitespace-only lines are dropped. Persistent overrides - should be added to this default in a PR. + Blank/whitespace-only lines are dropped. Leading/trailing + whitespace around an entry's key and value is not trimmed + here — vuls2's override parser trims both sides, so it has no + effect on the resolved override. Persistent overrides should + be added to this default in a PR. required: false type: string default: "" From 3c2ec4e36caa8b4a436879d71ad3273ddbec2ba9 Mon Sep 17 00:00:00 2001 From: Shunichi Shinohara Date: Wed, 20 May 2026 12:22:40 +0900 Subject: [PATCH 09/10] chore(ci/diff-guard): make the parse step's fail-fast explicit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The `Parse threshold overrides` step relied on GitHub's `shell: bash` default (`bash --noprofile --norc -eo pipefail`) for fail-fast on an `awk`/`paste` failure. That works, but it left the contract implicit and reviewers (reasonably) flagged it. Add an explicit `set -euo pipefail` at the top of the run block, with a comment noting it just makes the runner default visible at the call site. If the parsing pipeline ever fails the step now aborts loudly instead of emitting empty overrides — the behavior is unchanged, only made explicit. Reported by copilot-pull-request-reviewer on #146. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/actions/diff-guard/action.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/actions/diff-guard/action.yml b/.github/actions/diff-guard/action.yml index b49a196..5fb8466 100644 --- a/.github/actions/diff-guard/action.yml +++ b/.github/actions/diff-guard/action.yml @@ -173,6 +173,11 @@ runs: DETECTION_OVERRIDES: ${{ inputs.detection_change_rate_threshold_overrides }} DB_OVERRIDES: ${{ inputs.db_change_rate_threshold_overrides }} run: | + # GitHub's `shell: bash` already runs with `-eo pipefail`; set it + # explicitly so the fail-fast contract is visible at the call site + # and not reliant on the runner default — if `awk`/`paste` fail + # the step aborts instead of silently emitting empty overrides. + set -euo pipefail detection_csv=$(printf '%s' "$DETECTION_OVERRIDES" | awk 'NF' | paste -sd, -) db_csv=$(printf '%s' "$DB_OVERRIDES" | awk 'NF' | paste -sd, -) # printf, not echo: a value beginning with `-` (e.g. `-n`) or From b2492f49d3be14eae3c229802ea271ca63b94280 Mon Sep 17 00:00:00 2001 From: Shunichi Shinohara Date: Wed, 20 May 2026 12:39:00 +0900 Subject: [PATCH 10/10] chore(ci/db-nightly): declare type: string on the threshold inputs The `_overrides` workflow_dispatch inputs were declared `type: string` while the pre-existing `db_change_rate_threshold` / `detection_change_rate_threshold` inputs omitted `type`. Add an explicit `type: string` to both so all four override/threshold inputs share one schema shape and the dispatch form does not drift. Reported by copilot-pull-request-reviewer on #146. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/db-nightly.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/db-nightly.yml b/.github/workflows/db-nightly.yml index 9c0ec8c..97beb0c 100644 --- a/.github/workflows/db-nightly.yml +++ b/.github/workflows/db-nightly.yml @@ -8,6 +8,7 @@ on: db_change_rate_threshold: description: "`vuls diff db` default change rate (%) threshold per ecosystem; diff fails if exceeded" required: false + type: string default: "10" db_change_rate_threshold_overrides: description: | @@ -25,6 +26,7 @@ on: detection_change_rate_threshold: description: "`vuls diff detection` default change rate (%) threshold per scan result; diff fails if exceeded" required: false + type: string default: "5" detection_change_rate_threshold_overrides: description: |