App-Project-Doctor
view release on metacpan or search on metacpan
lib/App/Project/Doctor/Check/Security.pm view on Meta::CPAN
package App::Project::Doctor::Check::Security;
use strict;
use warnings;
use autodie qw(:all);
use parent -norequire, 'App::Project::Doctor::Check::Base';
use Carp qw(croak carp);
use Readonly;
our $VERSION = '0.02';
Readonly::Array my @SECRET_PATTERNS => (
qr/(?:password|passwd|secret|api_?key|token)\s*=\s*['"][^'"]{4,}['"]/i,
qr/-----BEGIN (?:RSA |EC )?PRIVATE KEY-----/,
qr/(?:AKIA|ASIA)[A-Z0-9]{16}/,
);
sub name { 'Security' }
sub description { 'All modules declare strict/warnings; no hardcoded credentials.' }
sub can_fix { 1 }
sub order { 60 }
sub check {
my ($self, $ctx) = @_;
croak 'check requires an App::Project::Doctor::Context' unless ref $ctx;
my @findings;
my $files = $ctx->perl_files('lib', 'script', 'bin');
for my $rel (@{$files}) {
my $content = eval { $ctx->slurp($rel) } // do { carp "Cannot slurp $rel"; next };
# strict / warnings -- skip .t files (they inherit from test harness).
unless ($rel =~ /\.t$/) {
unless ($content =~ /^\s*use\s+strict\b/m) {
push @findings, _f(
severity => 'error',
message => "Missing 'use strict' in $rel.",
file => $rel,
fix => _fix_pragma($ctx, $rel, 'strict'),
);
}
unless ($content =~ /^\s*use\s+warnings\b/m) {
push @findings, _f(
severity => 'error',
message => "Missing 'use warnings' in $rel.",
file => $rel,
fix => _fix_pragma($ctx, $rel, 'warnings'),
);
}
}
# Credential scan.
my @lines = split /\n/, $content;
for my $i (0 .. $#lines) {
for my $pat (@SECRET_PATTERNS) {
if ($lines[$i] =~ $pat) {
push @findings, _f(
severity => 'error',
message => "Possible hardcoded credential in $rel at line " . ($i + 1) . '.',
file => $rel,
line => $i + 1,
detail => 'Move secrets to environment variables or a config file.',
);
last;
}
}
}
}
unless (@findings) {
push @findings, _f(
severity => 'pass',
message => 'All checked files use strict/warnings; no credential patterns found.',
);
}
return @findings;
}
( run in 0.530 second using v1.01-cache-2.11-cpan-817d5f8af8b )