From b82ee86b7bec45f12f80df2e88d9ce0346e44519 Mon Sep 17 00:00:00 2001 From: noctuum <25441068+noctuum@users.noreply.github.com> Date: Wed, 21 Oct 2026 09:38:39 +0500 Subject: [PATCH] fix(strategy): take scrape captures without sed validate-pkg.sh compiles the pattern with bash as the strategy does. --- docs/WORKFLOW.md | 4 +++- scripts/lib.sh | 24 ++++++++++++++++++++++++ scripts/validate-pkg.sh | 6 ++---- strategies/webpage-scrape.sh | 5 ++--- tests/strategies.bats | 13 ++++++++++++- tests/validate-pkg.bats | 21 +++++++++++++++++++++ 6 files changed, 64 insertions(+), 9 deletions(-) diff --git a/docs/WORKFLOW.md b/docs/WORKFLOW.md index 48d5974..ca7ef03 100644 --- a/docs/WORKFLOW.md +++ b/docs/WORKFLOW.md @@ -494,9 +494,11 @@ upstream: 1. Fetches the page: HTTPS only, at most 5 redirects and 5 MB. 2. Finds every match with `grep -oE`. -3. Extracts the first capture group with `sed -E`. +3. Takes the first capture group of each match with bash `[[ =~ ]]`, so the pattern may hold any character, `/` included. 4. Sorts with `sort -Vu` and returns the highest version. +`validate-pkg.sh` compiles the pattern with bash, which refuses some patterns that `grep -E` takes, `(?:...)` among them. + **Example YAML:** ```yaml diff --git a/scripts/lib.sh b/scripts/lib.sh index 2f704ec..1ad8b6c 100644 --- a/scripts/lib.sh +++ b/scripts/lib.sh @@ -133,6 +133,30 @@ release_tags() { grep -E '^v?[0-9]+(\.[0-9]+)*$' <<<"$1" || true } +# The first capture group of every match of an ERE in stdin, one per line. +# grep finds the matches and bash takes the group, so no delimiter of a sed +# command limits the pattern. +# Args: $1=ERE with a capture group +capture_matches() { + local pattern="$1" match + while IFS= read -r match; do + if [[ "$match" =~ $pattern ]]; then + printf '%s\n' "${BASH_REMATCH[1]}" + fi + done < <(grep -oE -e "$pattern") +} + +# Succeeds when bash accepts an ERE, as capture_matches needs. bash refuses +# some patterns that grep -E takes, (?:x) and *x among them. +# Args: $1=ERE +ere_valid() { + local rc=0 + # [[ =~ ]] returns 2 for a pattern bash cannot compile + # shellcheck disable=SC2319 + [[ "" =~ $1 ]] 2>/dev/null || rc=$? + ((rc != 2)) +} + # ============================================================================= # CHECKSUMS # ============================================================================= diff --git a/scripts/validate-pkg.sh b/scripts/validate-pkg.sh index 45eb89d..f89731f 100755 --- a/scripts/validate-pkg.sh +++ b/scripts/validate-pkg.sh @@ -209,10 +209,8 @@ validate_pkg() { log_err "$file: strategy 'webpage-scrape' requires 'upstream.pattern'" ((errors++)) else - # Validate regex syntax: grep exits 2 on invalid regex - local grep_rc=0 - echo "" | grep -qE -e "$pattern" 2>/dev/null || grep_rc=$? - if [[ "$grep_rc" -eq 2 ]]; then + # The strategy runs the pattern through grep and bash + if ! ere_valid "$pattern"; then log_err "$file: upstream.pattern is not valid ERE regex: $pattern" ((errors++)) fi diff --git a/strategies/webpage-scrape.sh b/strategies/webpage-scrape.sh index 54c83fd..76a5367 100755 --- a/strategies/webpage-scrape.sh +++ b/strategies/webpage-scrape.sh @@ -41,9 +41,8 @@ page=$(http_get "$url" --max-redirs 5 --max-filesize "$MAX_SIZE" -A "Mozilla/5.0 exit 1 } -# Extract versions using ERE, capture group via sed. The pattern can close -# the s command; --sandbox refuses e, r and w -versions=$(echo "$page" | grep -oE -e "$pattern" | sed --sandbox -E "s/$pattern/\\1/" | sort -Vu) +# The first capture group of every match +versions=$(echo "$page" | capture_matches "$pattern" | sort -Vu) if [[ -z "$versions" ]]; then echo "No versions matched pattern '$pattern' at $url" >&2 diff --git a/tests/strategies.bats b/tests/strategies.bats index 4b11868..9dbfea7 100644 --- a/tests/strategies.bats +++ b/tests/strategies.bats @@ -246,6 +246,16 @@ teardown() { [ "$output" = "7.0.0" ] } +@test "webpage-scrape: a pattern may hold a slash" { + create_test_package "test-scrape-path" "webpage-scrape" "upstream: + url: https://example.com/releases + pattern: 'download/test-scrape-([0-9.]+)\\.tar\\.gz'" + + run "$TEST_TMPDIR/strategies/webpage-scrape.sh" "test-scrape-path" + [ "$status" -eq 0 ] + [ "$output" = "7.0.0" ] +} + # === git-latest === @test "git-latest: returns rCOUNT.HASH format from git repo" { @@ -384,8 +394,9 @@ teardown() { cd "$TEST_TMPDIR" run "$TEST_TMPDIR/strategies/webpage-scrape.sh" "test-scrape-exec" - [ "$status" -ne 0 ] [ ! -e "$TEST_TMPDIR/pwned" ] + [ "$status" -eq 0 ] + [ "$output" = "7" ] } @test "webpage-scrape: a pattern may start with a dash" { diff --git a/tests/validate-pkg.bats b/tests/validate-pkg.bats index 8d92b36..ee0e288 100644 --- a/tests/validate-pkg.bats +++ b/tests/validate-pkg.bats @@ -389,6 +389,27 @@ EOF [ "$status" -eq 0 ] } +# grep -E takes these and bash refuses them +@test "validate: webpage-scrape rejects a pattern that bash cannot compile" { + local pattern + for pattern in '(?:v)?pkg-([0-9.]+)' '*pkg-([0-9.]+)' 'pkg-([0-9]{1)'; do + create_test_package "mypkg" "webpage-scrape" "upstream: + url: https://example.com/releases + pattern: '$pattern'" + run "$TEST_TMPDIR/scripts/validate-pkg.sh" "mypkg" + [ "$status" -ne 0 ] + [[ "$output" == *"not valid ERE regex"* ]] + done +} + +@test "validate: webpage-scrape accepts a pattern with a slash" { + create_test_package "mypkg" "webpage-scrape" "upstream: + url: https://example.com/releases + pattern: 'download/pkg-([0-9.]+)\\.tar\\.gz'" + run "$TEST_TMPDIR/scripts/validate-pkg.sh" "mypkg" + [ "$status" -eq 0 ] +} + @test "validate: webpage-scrape rejects invalid regex pattern" { create_test_package "mypkg" "webpage-scrape" "upstream: url: https://example.com/releases