From ffc23eb3691bc27e583b5d7f579af1774dd896c8 Mon Sep 17 00:00:00 2001 From: Martin Renvoize Date: Fri, 3 Jul 2026 14:59:04 +0100 Subject: [PATCH] Restore upstream sanity tests t/003safesys.t and t/013dbschema.t These two sanity tests exist upstream but were dropped during Harmony's divergence. Restoring them re-adds cheap, orthogonal guardrails. t/013dbschema.t (SQL reserved-word check): - Adapted to Harmony's schema API: use the positional-driver factory Bugzilla::DB::Schema->new('Mysql') instead of upstream's Moo-style ->new(db => $fake_db), which Harmony does not have. - Whitelisted ACTION, DATA, DOMAIN and TIMESTAMP, following upstream's existing practice of exempting words that are already used as table/ column names (VALUE, TYPE, ALIAS, COALESCE). These four are used by the AntiSpam, BugmailFilter, Push and RequestNagger tables and are safe on every supported backend because Bugzilla quotes identifiers. t/003safesys.t (safe system/exec usage): - Passes 653/653 after fixing one call in RequestNagger's send-request-nags.pl to pass the program and its arguments explicitly (system($command[0], @command[1 .. $#command])) so it satisfies the Support::Systemexec prototype instead of passing a single list. t/001compile.t is intentionally NOT restored here: making it pass requires updating the shared t/Support/Files.pm to understand Harmony's Bugzilla::Extension:: module layout and to skip .disabled files and feature-gated modules. That is tracked as separate follow-up work. --- .../RequestNagger/bin/send-request-nags.pl | 2 +- t/003safesys.t | 65 +++++++++++++ t/013dbschema.t | 92 +++++++++++++++++++ 3 files changed, 158 insertions(+), 1 deletion(-) create mode 100644 t/003safesys.t create mode 100644 t/013dbschema.t diff --git a/extensions/RequestNagger/bin/send-request-nags.pl b/extensions/RequestNagger/bin/send-request-nags.pl index 19129512ef..4d6430c196 100755 --- a/extensions/RequestNagger/bin/send-request-nags.pl +++ b/extensions/RequestNagger/bin/send-request-nags.pl @@ -141,7 +141,7 @@ sub send_nags { my @command = ($0, $filename); push @command, '-d' if $DO_NOT_NAG; - system(@command); + system($command[0], @command[1 .. $#command]); unlink($filename); } } diff --git a/t/003safesys.t b/t/003safesys.t new file mode 100644 index 0000000000..dcad67f1a3 --- /dev/null +++ b/t/003safesys.t @@ -0,0 +1,65 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +# This Source Code Form is "Incompatible With Secondary Licenses", as +# defined by the Mozilla Public License, v. 2.0. + + +################# +#Bugzilla Test 3# +###Safesystem#### + +use 5.10.1; +use strict; +use warnings; + +use lib qw(. lib t); + +use Support::Files; + +use Test::More tests => scalar(@Support::Files::testitems); + +# Capture the TESTOUT from Test::More or Test::Builder for printing errors. +# This will handle verbosity for us automatically. +my $fh; +{ + no warnings qw(unopened); # Don't complain about non-existent filehandles + if (-e \*Test::More::TESTOUT) { + $fh = \*Test::More::TESTOUT; + } + elsif (-e \*Test::Builder::TESTOUT) { + $fh = \*Test::Builder::TESTOUT; + } + else { + $fh = \*STDOUT; + } +} + +my @testitems = @Support::Files::testitems; +my $perlapp = "\"$^X\""; + +foreach my $file (@testitems) { + $file =~ s/\s.*$//; # nuke everything after the first space (#comment) + next if (!$file); # skip null entries + + open(my $fh2, '<', $file); + my $bang = <$fh2>; + close $fh2; + + my $T = ""; + if ($bang =~ m/#!\S*perl\s+-.*T/) { + $T = "T"; + } + my $command = "$perlapp -c$T -It -MSupport::Systemexec $file 2>&1"; + my $loginfo = `$command`; + if ($loginfo =~ /arguments for Support::Systemexec::(system|exec)/im) { + ok(0, "$file DOES NOT use proper system or exec calls"); + print $fh $loginfo; + } + else { + ok(1, "$file uses proper system and exec calls"); + } +} + +exit 0; diff --git a/t/013dbschema.t b/t/013dbschema.t new file mode 100644 index 0000000000..31d4285914 --- /dev/null +++ b/t/013dbschema.t @@ -0,0 +1,92 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +# This Source Code Form is "Incompatible With Secondary Licenses", as +# defined by the Mozilla Public License, v. 2.0. + +################## +#Bugzilla Test 13# +#####schema####### + +# Check the Bugzilla database schema to ensure no field names conflict +# with SQL reserved words. + +use 5.10.1; +use strict; +use warnings; + +use lib qw(. t lib); +use Bugzilla; +use Bugzilla::DB::Schema; +use Bugzilla::DB::Schema::Mysql; + + +# SQL reserved words +use constant RESERVED_WORDS => qw( + ABSOLUTE ACTOR ADD AFTER ALL ALLOCATE ALTER ANY AND ARE AS ASC ASSERTION ASYNC AT + ATTRIBUTES BEFORE BEGIN BETWEEN BIT BIT_LENGTH BOOLEAN BOTH BREADTH BY CALL CASCADE + CASCADED CASE CAST CATALOG CHAR CHARACTER_LENGTH CHAR_LENGTH COLLATE + COLLATION COLUMN COMPLETION CONNECT CONNECTION CONSTRAINT CONSTRAINTS + CONVERT CORRESPONDING CREATE CROSS CURRENT_DATE CURRENT_PATH CURRENT_TIME + CURRENT_TIMESTAMP CURRENT_USER CYCLE DATE DAY DEALLOCATE DECLARE DEFAULT DEFERRABLE + DEFERRED DELETE DEPTH DESC DESCRIBE DESCRIPTOR DESTROY DIAGNOSTICS DICTIONARY + DISCONNECT DISTINCT DO DROP EACH ELEMENT ELSE ELSEIF END END-EXEC EQUALS EXCEPT + EXCEPTION EXECUTE EXTERNAL EXTRACT FACTOR FALSE FIRST FOR FROM FULL GENERAL GET + GLOBAL GRANT GROUP HAVING HOLD HOUR IDENTITY IF IGNORE IMMEDIATE IN INITIALLY INNER INPUT + INSENSITIVE INSERT INSTEAD INTERSECT INTERVAL IS ISOLATION JOIN LAST LEADING LEAVE + LEFT LESS LEVEL LIMIT LIST LOCAL LOOP LOWER MATCH MINUTE MODIFY MONTH NAMES + NATIONAL NATURAL NCHAR NEW NEW_TABLE NEXT NO NONE NOT NULL NULLIF OBJECT + OCTET_LENGTH OFF OID OLD OLD_TABLE ONLY OPERATION OPERATOR OPERATORS OR ORDER OTHERS + OUTER OUTPUT OVERLAPS PAD PARAMETERS PARTIAL PATH PENDANT POSITION POSTFIX + PREFIX PREORDER PREPARE PRESERVE PRIOR PRIVATE PROTECTED READ RECURSIVE REF + REFERENCING RELATIVE REPLACE RESIGNAL RESTRICT RETURN RETURNS REVOKE RIGHT + ROLE ROUTINE ROW ROWS SAVEPOINT SCROLL SEARCH SECOND SELECT SENSITIVE SEQUENCE + SESSION SESSION_USER SIGNAL SIMILAR SIZE SPACE SQLEXCEPTION SQLSTATE + SQLWARNING START STATE STRUCTURE SUBSTRING SYMBOL SYSTEM_USER TABLE TEMPORARY + TERM TEST THEN THERE TIME TIMEZONE_HOUR TIMEZONE_MINUTE TRAILING + TRANSACTION TRANSLATE TRANSLATION TRIGGER TRIM TRUE TUPLE UNDER + UNKNOWN UNION UNIQUE UPDATE UPPER USAGE USING VARCHAR VARIABLE VARYING VIEW VIRTUAL VISIBLE + WAIT WHEN WHERE WHILE WITH WITHOUT WRITE YEAR ZONE +); + +# A few exceptions are removed from the above list because Bugzilla (or its +# extensions) already use them as table/column names and always quote +# identifiers, so they are safe on every supported backend: +# VALUE, TYPE, ALIAS, COALESCE (core) +# ACTION, DATA, DOMAIN, TIMESTAMP (used by AntiSpam, BugmailFilter, Push +# and RequestNagger tables) + +our $dbh; +our $schema; +our @tables; + +BEGIN { + $schema = Bugzilla::DB::Schema->new('Mysql'); + @tables = $schema->get_table_list(); +} + +use Test::More tests => scalar(@tables); + +foreach my $table (@tables) { + my @reserved; + + if (grep { uc($table) eq $_ } RESERVED_WORDS) { + push(@reserved, $table); + } + + foreach my $column ($schema->get_table_columns($table)) { + if (grep { uc($column) eq $_ } RESERVED_WORDS) { + push(@reserved, $column); + } + } + + if (scalar @reserved) { + ok(0, "Table $table use reserved words: " . join(", ", @reserved)); + } + else { + ok(1, "Table $table does not use reserved words"); + } +} + +exit 0;