[2/4] scripts: add "scan" mode to process-advisories.sh to find more backports

Message ID PXBPLlPuRIC3YAjeHADlAw@emlix.com (mailing list archive)
State Changes Requested
Headers
Series [1/4] advisories: fix typo in README |

Commit Message

Rolf Eike Beer Aug. 21, 2026, 5:40 a.m. UTC
  This will look through the stable branches to find additional backports of 
the fix for the given advisory.

Signed-off-by: Rolf Eike Beer <eb@emlix.com>
---
 scripts/process-advisories.sh | 34 ++++++++++++++++++++++++++++++++--
 1 file changed, 32 insertions(+), 2 deletions(-)
  

Comments

Siddhesh Poyarekar Aug. 21, 2026, 3:20 p.m. UTC | #1
On 2026-08-21 01:40, Rolf Eike Beer wrote:
> This will look through the stable branches to find additional backports of
> the fix for the given advisory.

That's neat, thank you for contributing this.  Just some suggested 
changes below.

> 
> Signed-off-by: Rolf Eike Beer <eb@emlix.com>
> ---
>   scripts/process-advisories.sh | 34 ++++++++++++++++++++++++++++++++--
>   1 file changed, 32 insertions(+), 2 deletions(-)
> 
> diff --git a/scripts/process-advisories.sh b/scripts/process-advisories.sh
> index a520fab5e6..00a8cd9416 100755
> --- a/scripts/process-advisories.sh
> +++ b/scripts/process-advisories.sh
> @@ -25,7 +25,7 @@ command=$1
>   
>   usage () {
>       cat >&2 <<EOF
> -usage: $0 {update|news}
> +usage: $0 {update|news|scan}

Maybe give it a more precise name, e.g. update-backports ?

>   EOF
>       exit 1
>   }
> @@ -33,7 +33,7 @@ EOF
>   command="$1"
>   
>   case "$command" in
> -    update|news)
> +    update|news|scan)
>       ;;
>       *)
>           usage
> @@ -69,6 +69,36 @@ advisories_update() {
>       done
>   }
>   
> +advisories_scan() {
> +  advisory=$1
> +
> +  if [ -z $1 ]; then
> +    echo "Usage: $0 update GLIBC-SA-YYYY-NNNN"
> +    exit 1
> +  fi
> +
> +  advisory_file=advisories/$advisory
> +
> +  FIX=$(sed -nr '/^Fix-Commit: /s/Fix-Commit: *([0-9a-fA-F]+) *\(2\.[0-9]+\).*/\1/p' $advisory_file)
> +
> +  if [ -z "${FIX}" ]; then
> +    echo "No Fix-Commit found in $advisory_file"
> +    exit 1
> +  fi

There could be multiple Fix-Commit entries on the same branch, which 
means you likely want to search for all of those commits on older branches.

Also, since backports also show up as `Fix-Commit:` with a different 
branch name, you might want to restrict the commits to those that exist 
on the master branch.

> +  for n in $(seq 20 44); do
> +    BACKPORT=($(git log ..origin/release/2.${n}/master --grep "cherry picked from commit ${FIX}"   --format=%H))
> +    if [ ${#BACKPORT[@]} -eq 0 ]; then
> +      continue
> +    fi
> +    if [ ${#BACKPORT[@]} -ne 1 ]; then
> +      echo "Multiple matches for backport of ${FIX} found in branch 2.${n}"
> +      continue

This sounds like a repo inconsistency; one shouldn't have multiple 
matches for a cherry pick of the same commit in a branch.  Maybe flag an 
error here?

> +    fi
> +    grep -q "^Fix-Commit: ${BACKPORT[0]} " $advisory_file || echo "Fix-Commit: ${BACKPORT[0]} ($(get_rel ${BACKPORT[0]}))" >> $advisory_file

Hmm, if the process errors out in between for some reason, you may end 
up with a partially updated advisory file.  That won't be a problem 
though, since the partially updated advisory file is still consistent 
and a subsequent run should be able to continue with it, as long as you 
have the check for existence of the commit on the master branch, as I 
suggested above.

> +  done
> +}
> +
>   advisories_news() {
>     rel=$(get_rel "HEAD")
>     for f in $(grep -l "^Fix-Commit: .* ($rel)$" advisories/*); do

Also, I wonder if looking for the CVE id would be a better way of 
looking for the backports, especially if there are custom fixes on older 
branches.  It's probably not a common problem though.

Thanks,
Sid
  
Adhemerval Zanella Netto Aug. 21, 2026, 3:41 p.m. UTC | #2
On 21/08/26 02:40, Rolf Eike Beer wrote:
> This will look through the stable branches to find additional backports of 
> the fix for the given advisory.
> 
> Signed-off-by: Rolf Eike Beer <eb@emlix.com>

Describe this new option and the workflow change on the advisories/README.

> ---
>  scripts/process-advisories.sh | 34 ++++++++++++++++++++++++++++++++--
>  1 file changed, 32 insertions(+), 2 deletions(-)
> 
> diff --git a/scripts/process-advisories.sh b/scripts/process-advisories.sh
> index a520fab5e6..00a8cd9416 100755
> --- a/scripts/process-advisories.sh
> +++ b/scripts/process-advisories.sh
> @@ -25,7 +25,7 @@ command=$1
>  
>  usage () {
>      cat >&2 <<EOF
> -usage: $0 {update|news}
> +usage: $0 {update|news|scan}
>  EOF
>      exit 1
>  }
> @@ -33,7 +33,7 @@ EOF
>  command="$1"
>  
>  case "$command" in
> -    update|news)
> +    update|news|scan)
>      ;;
>      *)
>          usage
> @@ -69,6 +69,36 @@ advisories_update() {
>      done
>  }
>  
> +advisories_scan() {
> +  advisory=$1
> +
> +  if [ -z $1 ]; then
> +    echo "Usage: $0 update GLIBC-SA-YYYY-NNNN"
> +    exit 1
> +  fi
> +
> +  advisory_file=advisories/$advisory
> +
> +  FIX=$(sed -nr '/^Fix-Commit: /s/Fix-Commit: *([0-9a-fA-F]+) *\(2\.[0-9]+\).*/\1/p' $advisory_file)

This breaks if an advisory has two master fix commits, FIX becomes a 
newline-separated list, and git treats the embedded newline as pattern OR.

For instance:

$ sed -nr '/^Fix-Commit: /s/Fix-Commit: *([0-9a-fA-F]+) *\(2\.[0-9]+\).*/\1/p' advisories/GLIBC-SA-2024-0006
b048a482f088e53144d26a61c390bed0210f49f2
7835b00dbce53c3c87bbbb1754a95fb5e58187aa

And thus:

$ ./scripts/process-advisories.sh scan GLIBC-SA-2024-0006
Multiple matches for backport of b048a482f088e53144d26a61c390bed0210f49f2
7835b00dbce53c3c87bbbb1754a95fb5e58187aa found in branch 2.31
Multiple matches for backport of b048a482f088e53144d26a61c390bed0210f49f2
7835b00dbce53c3c87bbbb1754a95fb5e58187aa found in branch 2.32
Multiple matches for backport of b048a482f088e53144d26a61c390bed0210f49f2
7835b00dbce53c3c87bbbb1754a95fb5e58187aa found in branch 2.33
Multiple matches for backport of b048a482f088e53144d26a61c390bed0210f49f2
7835b00dbce53c3c87bbbb1754a95fb5e58187aa found in branch 2.34
Multiple matches for backport of b048a482f088e53144d26a61c390bed0210f49f2
7835b00dbce53c3c87bbbb1754a95fb5e58187aa found in branch 2.35
Multiple matches for backport of b048a482f088e53144d26a61c390bed0210f49f2
7835b00dbce53c3c87bbbb1754a95fb5e58187aa found in branch 2.36
Multiple matches for backport of b048a482f088e53144d26a61c390bed0210f49f2
7835b00dbce53c3c87bbbb1754a95fb5e58187aa found in branch 2.37
Multiple matches for backport of b048a482f088e53144d26a61c390bed0210f49f2
7835b00dbce53c3c87bbbb1754a95fb5e58187aa found in branch 2.38
Multiple matches for backport of b048a482f088e53144d26a61c390bed0210f49f2
7835b00dbce53c3c87bbbb1754a95fb5e58187aa found in branch 2.39

I think you need to check each fix like:

  for fix in ${FIX}; do
    BACKPORT=($(git log ..origin/release/2.${n}/master --grep "cherry picked from commit ${fix}"   --format=%H))
    [...]
  done

Also, I think we should accept both bare *and( release-annotated master commits:

  # Master fix commits is either not yet annotated with a release version or
  # annotated with a plain "(2.NN)".  Backport entries carry a "(2.NN-MMM)"
  # subscript and must not be scanned for.
  FIX=$(sed -nr 's/^Fix-Commit: *([0-9a-fA-F]+) *(\(2\.[0-9]+\))? *$/\1/p' $advisory_file)

> +
> +  if [ -z "${FIX}" ]; then
> +    echo "No Fix-Commit found in $advisory_file"
> +    exit 1
> +  fi
> +
> +  for n in $(seq 20 44); do

This would require to update this file on each release. We can assume that
release branch will always follow the release/2*./master pattern and query
the available one as:

  branches=$(git for-each-ref --format='%(refname:short)' \
            'refs/remotes/origin/release/2.*/master')

And iterate as:

  for branch in ${branches}; do

> +    BACKPORT=($(git log ..origin/release/2.${n}/master --grep "cherry picked from commit ${FIX}"   --format=%H))

The return might be empty depending on current tree status, I think
it would be better to specify the script should always scan master
(where the fix always land first):

  BACKPORT=($(git log origin/master..${branch} --grep "cherry picked from commit ${fix}"   --format=%H))

> +    if [ ${#BACKPORT[@]} -eq 0 ]; then
> +      continue
> +    fi
> +    if [ ${#BACKPORT[@]} -ne 1 ]; then
> +      echo "Multiple matches for backport of ${FIX} found in branch 2.${n}"
> +      continue
> +    fi
> +    grep -q "^Fix-Commit: ${BACKPORT[0]} " $advisory_file || echo "Fix-Commit: ${BACKPORT[0]} ($(get_rel ${BACKPORT[0]}))" >> $advisory_file

The extra space after the pattern only matches lines where something follows the
hash. And Advisory files contain Fix-Commit lines in two shapes:

* Fix-Commit: 2ae9446c... (2.38-74) — the post-update shape and it has the expected
  space.

* Fix-Commit: 2ae9446c... - there is no trailing space to match, so grep reports 
  "not found".

We need to handle both cases:

  grep -qE "^Fix-Commit: ${BACKPORT[0]}( |\$)" $advisory_file || echo "Fix-Commit: ${BACKPORT[0]} ($(get_rel ${BACKPORT[0]}))" >> $advisory_file

> +  done
> +}
> +
>  advisories_news() {
>    rel=$(get_rel "HEAD")
>    for f in $(grep -l "^Fix-Commit: .* ($rel)$" advisories/*); do
  
Florian Weimer Aug. 21, 2026, 6:48 p.m. UTC | #3
* Rolf Eike Beer:

> This will look through the stable branches to find additional backports of 
> the fix for the given advisory.

Is time to convert this script to Python?

Thanks,
Florian
  
Rolf Eike Beer Sept. 11, 2026, 6:09 a.m. UTC | #4
On Freitag, 21. August 2026 07:40:41 Mitteleuropäische Sommerzeit Rolf Eike 
Beer wrote:
> This will look through the stable branches to find additional backports of
> the fix for the given advisory.

Thank you all for your review. What is the way forward, should I try to 
address the comments in this shell script or will it be converted to Python?

Regards,

Eike
  

Patch

diff --git a/scripts/process-advisories.sh b/scripts/process-advisories.sh
index a520fab5e6..00a8cd9416 100755
--- a/scripts/process-advisories.sh
+++ b/scripts/process-advisories.sh
@@ -25,7 +25,7 @@  command=$1
 
 usage () {
     cat >&2 <<EOF
-usage: $0 {update|news}
+usage: $0 {update|news|scan}
 EOF
     exit 1
 }
@@ -33,7 +33,7 @@  EOF
 command="$1"
 
 case "$command" in
-    update|news)
+    update|news|scan)
     ;;
     *)
         usage
@@ -69,6 +69,36 @@  advisories_update() {
     done
 }
 
+advisories_scan() {
+  advisory=$1
+
+  if [ -z $1 ]; then
+    echo "Usage: $0 update GLIBC-SA-YYYY-NNNN"
+    exit 1
+  fi
+
+  advisory_file=advisories/$advisory
+
+  FIX=$(sed -nr '/^Fix-Commit: /s/Fix-Commit: *([0-9a-fA-F]+) *\(2\.[0-9]+\).*/\1/p' $advisory_file)
+
+  if [ -z "${FIX}" ]; then
+    echo "No Fix-Commit found in $advisory_file"
+    exit 1
+  fi
+
+  for n in $(seq 20 44); do
+    BACKPORT=($(git log ..origin/release/2.${n}/master --grep "cherry picked from commit ${FIX}"   --format=%H))
+    if [ ${#BACKPORT[@]} -eq 0 ]; then
+      continue
+    fi
+    if [ ${#BACKPORT[@]} -ne 1 ]; then
+      echo "Multiple matches for backport of ${FIX} found in branch 2.${n}"
+      continue
+    fi
+    grep -q "^Fix-Commit: ${BACKPORT[0]} " $advisory_file || echo "Fix-Commit: ${BACKPORT[0]} ($(get_rel ${BACKPORT[0]}))" >> $advisory_file
+  done
+}
+
 advisories_news() {
   rel=$(get_rel "HEAD")
   for f in $(grep -l "^Fix-Commit: .* ($rel)$" advisories/*); do