Permalink
Browse files

Validate project names.

They must be valid names like changes and tags, but additionally may not
contain colons.
  • Loading branch information...
1 parent 4994207 commit a21cb640e973f67e19824c2fbabfc4b84538eeae @theory committed Aug 7, 2012
Showing with 91 additions and 13 deletions.
  1. +13 −1 lib/App/Sqitch/Command/init.pm
  2. +22 −11 lib/App/Sqitch/Plan.pm
  3. +37 −1 t/init.t
  4. +19 −0 t/plan.t
@@ -19,7 +19,7 @@ our $VERSION = '0.83';
sub execute {
my ( $self, $project ) = @_;
- $self->usage unless $project;
+ $self->_validate_project($project);
$self->write_config;
$self->write_plan($project);
$self->make_directories;
@@ -38,6 +38,18 @@ sub options {
);
}
+sub _validate_project {
+ my ( $self, $project ) = @_;
+ $self->usage unless $project;
+ hurl init => __x(
+ qq{invalid project name "{project}": project names must not }
+ . 'begin with punctuation, contain "@" or ":", or end in '
+ . 'punctuation or digits following punctuation',
+ project => $project
+ ) unless $project =~ /\A[^[:punct:]](?:[^[:blank:]:@]*[^[:punct:][:blank:]])?\z/
+ && $project !~ /[[:punct:]][[:digit:]]+\z/;
+}
+
sub configure {
my ( $class, $config, $opt ) = @_;
View
@@ -143,6 +143,16 @@ sub _parse {
<(?<planner_email>[^>]+)> # email
/x;
+ # Use for raising syntax error exceptions.
+ my $raise_syntax_error = sub {
+ hurl plan => __x(
+ 'Syntax error in {file} at line {lineno}: {error}',
+ file => $file,
+ lineno => $fh->input_line_number,
+ error => shift
+ );
+ };
+
LINE: while ( my $line = $fh->getline ) {
chomp $line;
@@ -183,6 +193,17 @@ sub _parse {
# future releases, may change parsers depending on the
# version.
$pragmas{syntax_version} = $params{value} = SYNTAX_VERSION;
+ } elsif ($+{name} eq 'project') {
+ my $proj = $+{value};
+ $raise_syntax_error->(__x(
+ qq{invalid project name "{project}": project names must not }
+ . 'begin with punctuation, contain "@" or ":", or end in '
+ . 'punctuation or digits following punctuation',
+ project => $proj,
+ )) unless $proj =~ /\A$name_re\z/
+ && $proj !~ /:/
+ && $proj !~ /[[:punct:]][[:digit:]]+\z/;
+ $pragmas{project} = $proj;
} else {
$pragmas{ $+{name} } = $+{value} // 1;
}
@@ -229,16 +250,6 @@ sub _parse {
%params = ( %params, %+ );
- # Make sure we have a valid name.
- my $raise_syntax_error = sub {
- hurl plan => __x(
- 'Syntax error in {file} at line {lineno}: {error}',
- file => $file,
- lineno => $fh->input_line_number,
- error => shift
- );
- };
-
# Raise errors for missing data.
$raise_syntax_error->(__(
'Invalid name; names must not begin or end in '
@@ -372,7 +383,7 @@ sub _parse {
$pragmas{syntax_version} = SYNTAX_VERSION;
}
- # Should have project pragma.
+ # Should have valid project pragma.
hurl plan => __x(
'Missing %project pragma in {file}',
file => $file,
View
@@ -4,7 +4,7 @@ use strict;
use warnings;
use v5.10.1;
use utf8;
-use Test::More tests => 122;
+use Test::More tests => 149;
#use Test::More 'no_plan';
use App::Sqitch;
use Locale::TextDomain qw(App-Sqitch);
@@ -462,6 +462,42 @@ file_contents_is $plan_file,
'%uri=' . $uri->canonical . "$/$/",
'The plan should include the project and uri pragmas';
+##############################################################################
+# Test _validate_project().
+can_ok $init, '_validate_project';
+NOPROJ: {
+ # Test handling of no command.
+ my $mock = Test::MockModule->new($CLASS);
+ my @args;
+ $mock->mock(usage => sub { @args = @_; die 'USAGE' });
+ throws_ok { $CLASS->_validate_project }
+ qr/USAGE/, 'No project should yield usage';
+ is_deeply \@args, [$CLASS], 'No args should be passed to usage';
+}
+
+# Test invalid project names.
+my @bad_names = (
+ '^foo', # No leading punctuation
+ 'foo+', # No trailing punctuation
+ 'foo+6', # No trailing punctuation+digit
+ 'foo+666', # No trailing punctuation+digits
+ '%hi', # No leading punctuation
+ 'hi!', # No trailing punctuation
+ 'foo@bar', # No @ allowed at all
+ 'foo:bar', # No : allowed at all
+);
+for my $bad (@bad_names) {
+ throws_ok { $init->_validate_project($bad) } 'App::Sqitch::X',
+ qq{Should get error for invalid project name "$bad"};
+ is $@->ident, 'init', qq{Bad project "$bad" ident should be "init"};
+ is $@->message, __x(
+ qq{invalid project name "{project}": project names must not }
+ . 'begin with punctuation, contain "@" or ":", or end in '
+ . 'punctuation or digits following punctuation',
+ project => $bad
+ ), qq{Bad project "$bad" error message should be correct};
+}
+
##############################################################################
# Bring it all together, yo.
$conf_file->remove;
View
@@ -1477,4 +1477,23 @@ is $@->ident, 'plan', 'Missing prorject error ident should be "plan"';
is $@->message, __x('Missing %project pragma in {file}', file => 'noproject'),
'The missing project error message should be correct';
+# Make sure we get an error for an invalid project name.
+for my $bad (@bad_names, 'foo:bar') {
+ my $fh = IO::File->new(\"%project=$bad\n\nfoo $tsnp", '<:utf8');
+ throws_ok { $plan->_parse(badproj => $fh) } 'App::Sqitch::X',
+ qq{Should die on invalid project name "$bad"};
+ is $@->ident, 'plan', qq{Ident for bad proj "$bad" should be "plan"};
+ is $@->message, __x(
+ 'Syntax error in {file} at line {line}: {error}',
+ file => 'badproj',
+ line => 1,
+ error => __x(
+ qq{invalid project name "{project}": project names must not }
+ . 'begin with punctuation, contain "@" or ":", or end in '
+ . 'punctuation or digits following punctuation',
+ project => $bad
+ ),
+ ), qq{Error message for bad project "$bad" should be correct};
+}
+
done_testing;

0 comments on commit a21cb64

Please sign in to comment.