feat: add full Zonemaster stack with Docker and Spanish UI
- Clone all 5 Zonemaster component repos (LDNS, Engine, CLI, Backend, GUI) - Dockerfile.backend: 8-stage multi-stage build LDNS→Engine→CLI→Backend - Dockerfile.gui: Astro static build served via nginx - docker-compose.yml: backend (internal) + frontend (port 5353) - nginx.conf: root redirects to /es/, /api/ proxied to backend - zonemaster-gui/config.ts: defaultLanguage set to 'es' (Spanish) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
215
zonemaster-backend/t/validator.t
Normal file
215
zonemaster-backend/t/validator.t
Normal file
@@ -0,0 +1,215 @@
|
||||
#!perl -T
|
||||
use strict;
|
||||
use warnings;
|
||||
use utf8;
|
||||
|
||||
use Test::More tests => 2;
|
||||
use Test::NoWarnings;
|
||||
use Test::Differences;
|
||||
use Scalar::Util qw( tainted );
|
||||
use JSON::Validator::Schema::Draft7;
|
||||
|
||||
# Get a tainted copy of a string
|
||||
sub taint {
|
||||
my ( $string ) = @_;
|
||||
|
||||
if ( !tainted $0 ) {
|
||||
BAIL_OUT( 'We need $0 to be tainted' );
|
||||
}
|
||||
|
||||
return substr $string . $0, length $0;
|
||||
}
|
||||
|
||||
sub compile_schema {
|
||||
my $jv = JSON::Validator::Schema::Draft7->new->coerce('booleans,numbers,strings')->data(@_);
|
||||
$jv->formats(Zonemaster::Backend::Validator::formats( undef ));
|
||||
return $jv;
|
||||
}
|
||||
|
||||
subtest 'Everything but NoWarnings' => sub {
|
||||
|
||||
use_ok( 'Zonemaster::Backend::Validator', ':untaint' );
|
||||
|
||||
subtest 'ds_info' => sub {
|
||||
my $v = compile_schema( Zonemaster::Backend::Validator->new->ds_info );
|
||||
my $ds_info_40 = { digest => '0' x 40, algorithm => 0, digtype => 0, keytag => 0 };
|
||||
my $ds_info_64 = { digest => '0' x 64, algorithm => 0, digtype => 0, keytag => 0 };
|
||||
eq_or_diff [ $v->validate( $ds_info_40 ) ], [], 'accept ds_info with 40-digit hash';
|
||||
eq_or_diff [ $v->validate( $ds_info_64 ) ], [], 'accept ds_info with 64-digit hash';
|
||||
};
|
||||
|
||||
subtest 'ip_address' => sub {
|
||||
my $v = compile_schema( Zonemaster::Backend::Validator->new->ip_address );
|
||||
eq_or_diff [ $v->validate( '192.168.0.2' ) ], [], 'accept: 192.168.0.2';
|
||||
eq_or_diff [ $v->validate( '2001:db8::1' ) ], [], 'accept: 2001:db8::1';
|
||||
};
|
||||
|
||||
subtest 'untaint_abs_path' => sub {
|
||||
is scalar untaint_abs_path( '/var/db/zonemaster.sqlite' ), '/var/db/zonemaster.sqlite', 'accept: /var/db/zonemaster.sqlite';
|
||||
is scalar untaint_abs_path( 'zonemaster.sqlite' ), undef, 'reject: zonemaster.sqlite';
|
||||
is scalar untaint_abs_path( './zonemaster.sqlite' ), undef, 'reject: ./zonemaster.sqlite';
|
||||
ok !tainted( untaint_abs_path( taint( 'localhost' ) ) ), 'launder taint';
|
||||
};
|
||||
|
||||
subtest 'untaint_engine_type' => sub {
|
||||
is scalar untaint_engine_type( 'MySQL' ), 'MySQL', 'accept: MySQL';
|
||||
is scalar untaint_engine_type( 'mysql' ), 'mysql', 'accept: mysql';
|
||||
is scalar untaint_engine_type( 'PostgreSQL' ), 'PostgreSQL', 'accept: PostgreSQL';
|
||||
is scalar untaint_engine_type( 'postgresql' ), 'postgresql', 'accept: postgresql';
|
||||
is scalar untaint_engine_type( 'SQLite' ), 'SQLite', 'accept: SQLite';
|
||||
is scalar untaint_engine_type( 'sqlite' ), 'sqlite', 'accept: sqlite';
|
||||
is scalar untaint_engine_type( 'Excel' ), undef, 'reject: Excel';
|
||||
ok !tainted( untaint_engine_type( taint( 'SQLite' ) ) ), 'launder taint';
|
||||
};
|
||||
|
||||
subtest 'untaint_ip_address' => sub {
|
||||
is scalar untaint_ip_address( '192.0.2.1' ), '192.0.2.1', 'accept: 192.0.2.1';
|
||||
is scalar untaint_ip_address( '192.0.2' ), undef, 'reject: 192.0.2';
|
||||
is scalar untaint_ip_address( '192' ), undef, 'reject: 192';
|
||||
is scalar untaint_ip_address( '192.0.2.1:3306' ), undef, 'reject: 192.0.2.1:3306';
|
||||
is scalar untaint_ip_address( '2001:db8::' ), '2001:db8::', 'accept: 2001:db8::';
|
||||
is scalar untaint_ip_address( '2001:db8::/32' ), undef, 'reject: 2001:db8::/32';
|
||||
is scalar untaint_ip_address( '2001:db8:ffff:ffff:ffff:ffff:ffff:ffff' ), '2001:db8:ffff:ffff:ffff:ffff:ffff:ffff', 'accept: 2001:db8:ffff:ffff:ffff:ffff:ffff:ffff';
|
||||
is scalar untaint_ip_address( '2001:db8:ffff:ffff:ffff:ffff:ffff' ), undef, 'reject: 2001:db8:ffff:ffff:ffff:ffff:ffff';
|
||||
is scalar untaint_ip_address( '2001:db8::255.255.255.254' ), '2001:db8::255.255.255.254', 'accept: 2001:db8::255.255.255.254';
|
||||
is scalar untaint_ip_address( '2001:db8::255.255.255' ), undef, 'reject: 2001:db8::255.255.255';
|
||||
is scalar untaint_ip_address( '::1' ), '::1', 'accept: ::1';
|
||||
is scalar untaint_ip_address( ':::1' ), undef, 'reject: :::1';
|
||||
ok !tainted( untaint_ip_address( taint( '192.0.2.1' ) ) ), 'launder taint';
|
||||
};
|
||||
|
||||
subtest 'untaint_ldh_domain' => sub {
|
||||
is scalar untaint_ldh_domain( 'localhost' ), 'localhost', 'accept: localhost';
|
||||
is scalar untaint_ldh_domain( 'example.com' ), 'example.com', 'accept: example.com';
|
||||
is scalar untaint_ldh_domain( 'example.com.' ), 'example.com.', 'accept: example.com.';
|
||||
is scalar untaint_ldh_domain( '192.0.2.1' ), '192.0.2.1', 'accept: 192.0.2.1';
|
||||
is scalar untaint_ldh_domain( '0/26.2.0.192.in-addr.arpa' ), '0/26.2.0.192.in-addr.arpa', 'accept: 0/26.2.0.192.in-addr.arpa';
|
||||
is scalar untaint_ldh_domain( '_http._tcp.example.com' ), '_http._tcp.example.com', 'accept: _http._tcp.example.com';
|
||||
is scalar untaint_ldh_domain( '192.0.2.1:3306' ), undef, 'reject: 192.0.2.1:3306';
|
||||
is scalar untaint_ldh_domain( '1!26.2.0.192.in-addr.arpa' ), undef, 'reject: 1!26.2.0.192.in-addr.arpa';
|
||||
is scalar untaint_ldh_domain( '$http.example.com' ), undef, 'reject: $http.example.com';
|
||||
ok !tainted( untaint_ldh_domain( taint( 'localhost' ) ) ), 'launder taint';
|
||||
};
|
||||
|
||||
subtest 'untaint_locale_tag' => sub {
|
||||
is scalar untaint_locale_tag( 'en_US' ), 'en_US', 'accept: en_US';
|
||||
is scalar untaint_locale_tag( 'en' ), undef, 'reject: en';
|
||||
is scalar untaint_locale_tag( 'English' ), undef, 'reject: English';
|
||||
ok !tainted( untaint_locale_tag( taint( 'en_US' ) ) ), 'launder taint';
|
||||
};
|
||||
|
||||
subtest 'untaint_mariadb_database' => sub {
|
||||
is scalar untaint_mariadb_database( 'zonemaster' ), 'zonemaster', 'accept: zonemaster';
|
||||
is scalar untaint_mariadb_database( 'ZONEMASTER' ), 'ZONEMASTER', 'accept: ZONEMASTER';
|
||||
is scalar untaint_mariadb_database( 'dollar$' ), 'dollar$', 'accept: dollar$';
|
||||
is scalar untaint_mariadb_database( '$dollar' ), '$dollar', 'accept: $dollar';
|
||||
is scalar untaint_mariadb_database( '0zonemaster' ), '0zonemaster', 'accept: 0zonemaster';
|
||||
is scalar untaint_mariadb_database( 'zm_backend' ), 'zm_backend', 'accept: zm_backend';
|
||||
is scalar untaint_mariadb_database( 'zm backend' ), undef, 'reject: zm backend';
|
||||
is scalar untaint_mariadb_database( 'zm-backend' ), undef, 'reject: zm-backend';
|
||||
is scalar untaint_mariadb_database( '' ), undef, 'reject empty string';
|
||||
is scalar untaint_mariadb_database( 'zönemästër' ), undef, 'reject: zönemästër';
|
||||
is scalar untaint_mariadb_database( 'a' x 65 ), undef, 'reject 65 characters';
|
||||
is scalar untaint_mariadb_database( 'a' x 64 ), 'a' x 64, 'accept 64 characters';
|
||||
ok !tainted( untaint_mariadb_database( taint( 'zonemaster' ) ) ), 'launder taint';
|
||||
};
|
||||
|
||||
subtest 'untaint_mariadb_user' => sub {
|
||||
is scalar untaint_mariadb_user( 'zonemaster' ), 'zonemaster', 'accept: zonemaster';
|
||||
is scalar untaint_mariadb_user( 'ZONEMASTER' ), 'ZONEMASTER', 'accept: ZONEMASTER';
|
||||
is scalar untaint_mariadb_user( '$dollar' ), '$dollar', 'accept: $dollar';
|
||||
is scalar untaint_mariadb_user( '0zonemaster' ), '0zonemaster', 'accept: 0zonemaster';
|
||||
is scalar untaint_mariadb_user( 'zm_backend' ), 'zm_backend', 'accept: zm_backend';
|
||||
is scalar untaint_mariadb_user( 'zm backend' ), undef, 'reject: zm backend';
|
||||
is scalar untaint_mariadb_user( 'zm-backend' ), undef, 'reject: zm-backend';
|
||||
is scalar untaint_mariadb_user( '' ), undef, 'reject empty string';
|
||||
is scalar untaint_mariadb_user( 'zönemästër' ), undef, 'reject: zönemästër';
|
||||
is scalar untaint_mariadb_user( 'a' x 81 ), undef, 'reject 81 characters';
|
||||
is scalar untaint_mariadb_user( 'a' x 80 ), 'a' x 80, 'accept 80 characters';
|
||||
ok !tainted( untaint_mariadb_user( taint( 'zonemaster' ) ) ), 'launder taint';
|
||||
};
|
||||
|
||||
subtest 'untaint_password' => sub {
|
||||
is scalar untaint_password( '123456' ), '123456', 'accept: 123456';
|
||||
is scalar untaint_password( 'password' ), 'password', 'accept: password';
|
||||
is scalar untaint_password( '!@#$%^&*<' ), '!@#$%^&*<', 'accept: !@#$%^&*<';
|
||||
is scalar untaint_password( 'Qwertyuiop' ), 'Qwertyuiop', 'accept: Qwertyuiop';
|
||||
is scalar untaint_password( 'battery staple' ), 'battery staple', 'accept: battery staple';
|
||||
is scalar untaint_password( '' ), '', 'accept the empty string';
|
||||
is scalar untaint_password( "\t" ), undef, 'reject tab character';
|
||||
is scalar untaint_password( "\x80" ), undef, 'reject del character';
|
||||
is scalar untaint_password( ' x' ), undef, 'reject initial space';
|
||||
is scalar untaint_password( '<x' ), undef, 'reject initial <';
|
||||
is scalar untaint_password( 'åäö' ), undef, 'reject: åäö';
|
||||
is scalar untaint_password( 'a' x 100 ), 'a' x 100, 'accept 100 characters';
|
||||
is scalar untaint_password( 'a' x 101 ), undef, 'reject 101 characters';
|
||||
ok !tainted( untaint_password( taint( '123456' ) ) ), 'launder taint';
|
||||
};
|
||||
|
||||
subtest 'untaint_postgresql_ident' => sub {
|
||||
is scalar untaint_postgresql_ident( 'zonemaster' ), 'zonemaster', 'accept: zonemaster';
|
||||
is scalar untaint_postgresql_ident( 'ZONEMASTER' ), 'ZONEMASTER', 'accept: ZONEMASTER';
|
||||
is scalar untaint_postgresql_ident( 'zm_backend' ), 'zm_backend', 'accept: zm_backend';
|
||||
is scalar untaint_postgresql_ident( 'dollar$' ), 'dollar$', 'accept: dollar$';
|
||||
is scalar untaint_postgresql_ident( '$dollar' ), undef, 'reject: $dollar';
|
||||
is scalar untaint_postgresql_ident( 'zm backend' ), undef, 'reject: zm backend';
|
||||
is scalar untaint_postgresql_ident( '0zonemaster' ), undef, 'reject: 0zonemaster';
|
||||
is scalar untaint_postgresql_ident( 'zm-backend' ), undef, 'reject: zm-backend';
|
||||
is scalar untaint_postgresql_ident( '' ), undef, 'reject empty string';
|
||||
is scalar untaint_postgresql_ident( 'zönemästër' ), undef, 'reject: zönemästër';
|
||||
is scalar untaint_postgresql_ident( 'a' x 64 ), undef, 'reject 64 characters';
|
||||
is scalar untaint_postgresql_ident( 'a' x 63 ), 'a' x 63, 'accept 63 characters';
|
||||
ok !tainted( untaint_postgresql_ident( taint( 'zonemaster' ) ) ), 'launder taint';
|
||||
};
|
||||
|
||||
subtest 'untaint_profile_name' => sub {
|
||||
is scalar untaint_profile_name( 'default' ), 'default', 'accept: default';
|
||||
is scalar untaint_profile_name( '-leading-dash' ), undef, 'reject: -leading-dash';
|
||||
is scalar untaint_profile_name( 'trailing-dash-' ), undef, 'reject: trailing-dash-';
|
||||
is scalar untaint_profile_name( 'middle-dash' ), 'middle-dash', 'accept: middle-dash';
|
||||
is scalar untaint_profile_name( '_leading_underscore' ), undef, 'reject: _leading_underscore';
|
||||
is scalar untaint_profile_name( 'trailing_underscore_' ), undef, 'reject: trailing_underscore_';
|
||||
is scalar untaint_profile_name( 'middle_underscore' ), 'middle_underscore', 'accept: middle_underscore';
|
||||
is scalar untaint_profile_name( '0-leading-digit' ), '0-leading-digit', 'accept: 0-leading-digit';
|
||||
is scalar untaint_profile_name( 'a' ), 'a', 'accept: a';
|
||||
is scalar untaint_profile_name( '-' ), undef, 'reject dash';
|
||||
is scalar untaint_profile_name( '_' ), undef, 'reject underscore';
|
||||
is scalar untaint_profile_name( 'a' x 32 ), 'a' x 32, 'accept 32 characters';
|
||||
is scalar untaint_profile_name( 'a' x 33 ), undef, 'reject 33 characters';
|
||||
ok !tainted( untaint_profile_name( taint( 'default' ) ) ), 'launder taint';
|
||||
};
|
||||
|
||||
subtest 'untaint_non_negative_int' => sub {
|
||||
is scalar untaint_non_negative_int( '1' ), '1', 'accept: 1';
|
||||
is scalar untaint_non_negative_int( '0' ), '0', 'accept: 0';
|
||||
is scalar untaint_non_negative_int( '99999' ), '99999', 'accept: 99999';
|
||||
is scalar untaint_non_negative_int( '100000' ), undef, 'reject: 100000';
|
||||
is scalar untaint_non_negative_int( '0.5' ), undef, 'reject: 0.5';
|
||||
is scalar untaint_non_negative_int( '-1' ), undef, 'reject: -1';
|
||||
ok !tainted( untaint_non_negative_int( taint( '1' ) ) ), 'launder taint';
|
||||
};
|
||||
|
||||
subtest 'untaint_strictly_positive_int' => sub {
|
||||
is scalar untaint_strictly_positive_int( '1' ), '1', 'accept: 1';
|
||||
is scalar untaint_strictly_positive_int( '99999' ), '99999', 'accept: 99999';
|
||||
is scalar untaint_strictly_positive_int( '100000' ), undef, 'reject: 100000';
|
||||
is scalar untaint_strictly_positive_int( '0' ), undef, 'reject: 0';
|
||||
is scalar untaint_strictly_positive_int( '0.5' ), undef, 'reject: 0.5';
|
||||
is scalar untaint_strictly_positive_int( '-1' ), undef, 'reject: -1';
|
||||
ok !tainted( untaint_strictly_positive_int( taint( '1' ) ) ), 'launder taint';
|
||||
};
|
||||
|
||||
subtest 'untaint_strictly_positive_millis' => sub {
|
||||
is scalar untaint_strictly_positive_millis( '0.5' ), '0.5', 'accept: 0.5';
|
||||
is scalar untaint_strictly_positive_millis( '0.001' ), '0.001', 'accept: 0.001';
|
||||
is scalar untaint_strictly_positive_millis( '99999.999' ), '99999.999', 'accept: 99999.999';
|
||||
is scalar untaint_strictly_positive_millis( '1' ), '1', 'accept: 1';
|
||||
is scalar untaint_strictly_positive_millis( '99999' ), '99999', 'accept: 99999';
|
||||
is scalar untaint_strictly_positive_millis( '0.0009' ), undef, 'reject: 0.0009';
|
||||
is scalar untaint_strictly_positive_millis( '100000' ), undef, 'reject: 100000';
|
||||
is scalar untaint_strictly_positive_millis( '0' ), undef, 'reject: 0';
|
||||
is scalar untaint_strictly_positive_millis( '0.0' ), undef, 'reject: 0.0';
|
||||
is scalar untaint_strictly_positive_millis( '-1' ), undef, 'reject: -1';
|
||||
ok !tainted( untaint_strictly_positive_millis( taint( '0.5' ) ) ), 'launder taint';
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user