From 02af21f0a35fd6470ae78325138b7dba34df3c2a Mon Sep 17 00:00:00 2001 From: "David E. Wheeler" Date: Sat, 20 Jul 2024 15:51:03 -0400 Subject: [PATCH] Fix scanning of a pre-release starting with 0 A pre-release "Numeric identifiers MUST NOT include leading zeroes," according to SemVer 2.0. The existing code had accounted for this limitation: when it found a pre-release starting with a 0 followed by a digit, it would scan ahead and allow the value if it found an alphabetic character. There were two bugs in the Implementation. First, in addition to alphabetic characters, SemVer allows dashes. The [project regex] matches `[a-zA-Z-]`. So add a check for a dash in addition to `isalpha()`. Thanks to Dylan Bourque for the pull request (#70). The other bug was that the scanning was was not starting from the character after the zero and following digit. It was, instead, starting from that position subtracted from the length. For example, if the pre-release started at character 7 and the semver was 12 characters long, it would start scanning at character 13, well *after* the start of the pre-release. If the pre-release started at character 8 and the semver was 12 characters long, it would start scanning from character 4, well *before* the start of the pre-release. It's a wonder no errors were previously discovered. Fix it by starting from the current character, and include some details in Changes to help find existing bad values. --- Changes | 10 +++++ src/semver.c | 9 ++-- test/expected/corpus.out | 91 +++++++++++++++++++++------------------- test/sql/corpus.sql | 9 +++- 4 files changed, 70 insertions(+), 49 deletions(-) diff --git a/Changes b/Changes index d2d4ecb..92e9c31 100644 --- a/Changes +++ b/Changes @@ -6,6 +6,16 @@ Revision history for PostgreSQL extension semver. - Changed the tests to use `CREATE EXTENSION` instead of loading the SQL source file directory. The latter was required for Postgres 9.0 and lower, which have not been supported since v0.20.0 back in 2018. + - Fixed a bug that rejected pre-releases starting with a zero but + eventually include a dash, e.g., `1.2.3-02-3`. Thanks to Dylan + Bourque for the pull request (#70)! + - ***** WARNING: This release breaks compatibility with previous versions! ****** + Fixed a bug in the scanning of pre-releases that start with a zero. + It now properly scans the rest of the pre-release string. As a result, + existing semvers with pre-releases the start with a zero may have been + incorrectly interpreted as valid. See the v0.30.0 changes below for + instructions for finding such invalid semvers. Discovered while + debugging unexpected test failures in #70. 0.32.1 2023-08-01T23:20:31Z - Fixed compilation issue on Postgres 16. diff --git a/src/semver.c b/src/semver.c index 482f115..d63da30 100644 --- a/src/semver.c +++ b/src/semver.c @@ -5,6 +5,7 @@ * + Sam Vilain * + Tom Davis * + Xavier Caron + * + David Wheeler * * Copyright 2010-2024 The pg-semver Maintainers. This program is Free * Software; see the LICENSE file for the license conditions. @@ -212,14 +213,14 @@ semver* parse_semver(char* str, bool lax, bool throw, bool* bad) { if ((started_prerel || started_meta) && !skip_char) { if (i >= 1 && (i == 1 || patch[i-2] == '.') && patch[i-1] == '0' && isdigit(next)) { pred = true; - // Scan ahead. - for (p = len - atchar; p < len; p++) { + // Numeric identifiers must not include a leading 0. Scan ahead. + for (p = atchar; p < len; p++) { if (str[p] == '.') { // We got to the end of this bit. break; } - if (isalpha(str[p])) { - // If there is a letter, it's okay to start with a leading 0. + if (isalpha(str[p]) || str[p] == '-') { + // If there is a letter or a dash, it's okay to start with a leading 0. pred = false; break; } diff --git a/test/expected/corpus.out b/test/expected/corpus.out index 0debf85..528e516 100644 --- a/test/expected/corpus.out +++ b/test/expected/corpus.out @@ -1,5 +1,5 @@ \set ECHO none -1..71 +1..76 ok 1 - "0.0.4" is a valid semver ok 2 - "1.2.3" is a valid semver ok 3 - "10.20.30" is a valid semver @@ -30,46 +30,51 @@ ok 27 - "1.2.3----R-S.12.9.1--.12+meta" is a valid semver ok 28 - "1.2.3----RC-SNAPSHOT.12.9.1--.12" is a valid semver ok 29 - "1.0.0+0.build.1-rc.10000aaa-kk-0.1" is a valid semver ok 30 - "1.0.0-0A.is.legal" is a valid semver -not ok 31 - "99999999999999999999999.999999999999999999.99999999999999999" is a valid semver # TODO Large versions overflow integer bounds -# Failed (TODO) test 31: ""99999999999999999999999.999999999999999999.99999999999999999" is a valid semver" +ok 31 - "1.0.0-0010-1234" is a valid semver +ok 32 - "1.0.0-1.2.3-1234" is a valid semver +ok 33 - "1.0.0-1234" is a valid semver +ok 34 - "1.0.0-4321" is a valid semver +ok 35 - "0.0.0-00010101000000-000000000000" is a valid semver +not ok 36 - "99999999999999999999999.999999999999999999.99999999999999999" is a valid semver # TODO Large versions overflow integer bounds +# Failed (TODO) test 36: ""99999999999999999999999.999999999999999999.99999999999999999" is a valid semver" # died: XX000: bad semver value '99999999999999999999999.999999999999999999.99999999999999999': version number exceeds 31-bit range -ok 32 - "1" is not a valid semver -ok 33 - "1.2" is not a valid semver -ok 34 - "1.2.3-0123" is not a valid semver -ok 35 - "1.2.3-0123.0123" is not a valid semver -ok 36 - "1.1.2+.123" is not a valid semver -ok 37 - "+invalid" is not a valid semver -ok 38 - "-invalid" is not a valid semver -ok 39 - "-invalid+invalid" is not a valid semver -ok 40 - "-invalid.01" is not a valid semver -ok 41 - "alpha" is not a valid semver -ok 42 - "alpha.beta" is not a valid semver -ok 43 - "alpha.beta.1" is not a valid semver -ok 44 - "alpha.1" is not a valid semver -ok 45 - "alpha+beta" is not a valid semver -ok 46 - "alpha_beta" is not a valid semver -ok 47 - "alpha." is not a valid semver -ok 48 - "alpha.." is not a valid semver -ok 49 - "beta" is not a valid semver -ok 50 - "1.0.0-alpha_beta" is not a valid semver -ok 51 - "-alpha." is not a valid semver -ok 52 - "1.0.0-alpha.." is not a valid semver -ok 53 - "1.0.0-alpha..1" is not a valid semver -ok 54 - "1.0.0-alpha...1" is not a valid semver -ok 55 - "1.0.0-alpha....1" is not a valid semver -ok 56 - "1.0.0-alpha.....1" is not a valid semver -ok 57 - "1.0.0-alpha......1" is not a valid semver -ok 58 - "1.0.0-alpha.......1" is not a valid semver -ok 59 - "01.1.1" is not a valid semver -ok 60 - "1.01.1" is not a valid semver -ok 61 - "1.1.01" is not a valid semver -ok 62 - "1.2" is not a valid semver -ok 63 - "1.2.3.DEV" is not a valid semver -ok 64 - "1.2-SNAPSHOT" is not a valid semver -ok 65 - "1.2.31.2.3----RC-SNAPSHOT.12.09.1--..12+788" is not a valid semver -ok 66 - "1.2-RC-SNAPSHOT" is not a valid semver -ok 67 - "-1.0.3-gamma+b7718" is not a valid semver -ok 68 - "+justmeta" is not a valid semver -ok 69 - "9.8.7+meta+meta" is not a valid semver -ok 70 - "9.8.7-whatever+meta+meta" is not a valid semver -ok 71 - "99999999999999999999999.999999999999999999.99999999999999999----RC-SNAPSHOT.12.09.1--------------------------------..12" is not a valid semver +ok 37 - "1" is not a valid semver +ok 38 - "1.2" is not a valid semver +ok 39 - "1.2.3-0123" is not a valid semver +ok 40 - "1.2.3-0123.0123" is not a valid semver +ok 41 - "1.1.2+.123" is not a valid semver +ok 42 - "+invalid" is not a valid semver +ok 43 - "-invalid" is not a valid semver +ok 44 - "-invalid+invalid" is not a valid semver +ok 45 - "-invalid.01" is not a valid semver +ok 46 - "alpha" is not a valid semver +ok 47 - "alpha.beta" is not a valid semver +ok 48 - "alpha.beta.1" is not a valid semver +ok 49 - "alpha.1" is not a valid semver +ok 50 - "alpha+beta" is not a valid semver +ok 51 - "alpha_beta" is not a valid semver +ok 52 - "alpha." is not a valid semver +ok 53 - "alpha.." is not a valid semver +ok 54 - "beta" is not a valid semver +ok 55 - "1.0.0-alpha_beta" is not a valid semver +ok 56 - "-alpha." is not a valid semver +ok 57 - "1.0.0-alpha.." is not a valid semver +ok 58 - "1.0.0-alpha..1" is not a valid semver +ok 59 - "1.0.0-alpha...1" is not a valid semver +ok 60 - "1.0.0-alpha....1" is not a valid semver +ok 61 - "1.0.0-alpha.....1" is not a valid semver +ok 62 - "1.0.0-alpha......1" is not a valid semver +ok 63 - "1.0.0-alpha.......1" is not a valid semver +ok 64 - "01.1.1" is not a valid semver +ok 65 - "1.01.1" is not a valid semver +ok 66 - "1.1.01" is not a valid semver +ok 67 - "1.2" is not a valid semver +ok 68 - "1.2.3.DEV" is not a valid semver +ok 69 - "1.2-SNAPSHOT" is not a valid semver +ok 70 - "1.2.31.2.3----RC-SNAPSHOT.12.09.1--..12+788" is not a valid semver +ok 71 - "1.2-RC-SNAPSHOT" is not a valid semver +ok 72 - "-1.0.3-gamma+b7718" is not a valid semver +ok 73 - "+justmeta" is not a valid semver +ok 74 - "9.8.7+meta+meta" is not a valid semver +ok 75 - "9.8.7-whatever+meta+meta" is not a valid semver +ok 76 - "99999999999999999999999.999999999999999999.99999999999999999----RC-SNAPSHOT.12.09.1--------------------------------..12" is not a valid semver diff --git a/test/sql/corpus.sql b/test/sql/corpus.sql index b3fdd8b..c0fe98e 100644 --- a/test/sql/corpus.sql +++ b/test/sql/corpus.sql @@ -6,7 +6,7 @@ BEGIN; \i test/pgtap-core.sql CREATE EXTENSION semver; -SELECT plan(71); +SELECT plan(76); --SELECT * FROM no_plan(); -- Valid Semantic Versions @@ -43,7 +43,12 @@ SELECT lives_ok( '1.2.3----R-S.12.9.1--.12+meta', '1.2.3----RC-SNAPSHOT.12.9.1--.12', '1.0.0+0.build.1-rc.10000aaa-kk-0.1', - '1.0.0-0A.is.legal' + '1.0.0-0A.is.legal', + '1.0.0-0010-1234', + '1.0.0-1.2.3-1234', + '1.0.0-1234', + '1.0.0-4321', + '0.0.0-00010101000000-000000000000' -- ]) AS v;