view release on metacpan or search on metacpan
lib/Developer/Dashboard/CLI/OpenFile.pm view on Meta::CPAN
my ($basename) = $match_path =~ m{([^/\\]+)$};
$basename ||= $match_path;
my $stem = $basename;
$stem =~ s{\.[^.]+$}{};
my $rank = 0;
for my $pattern (@patterns) {
next if !defined $pattern || $pattern eq '';
my $regex = _compile_open_file_regex($pattern);
my $score = 50;
my @components = grep { defined && $_ ne '' } split m{[\\/]+}, $match_path;
if ( $basename =~ /\A(?:$pattern)\z/i ) {
$score = 0;
}
elsif ( $stem =~ /\A(?:$pattern)\z/i ) {
$score = 1;
}
elsif ( $basename =~ /\A(?:$pattern)/i ) {
$score = 2;
}
lib/Developer/Dashboard/CLI/OpenFile.pm view on Meta::CPAN
# _cached_archive_source_path(%args)
# Builds the stable cache location used for one extracted Java source member.
# Input: path registry object, archive file path string, and archive member path string.
# Output: extracted source file path string.
sub _cached_archive_source_path {
my (%args) = @_;
my $paths = $args{paths} || die 'Missing path registry';
my $archive = $args{archive} || die 'Missing archive path';
my $entry = $args{entry} || die 'Missing archive entry';
my $digest = md5_hex( join "\0", $archive, $entry );
my @parts = grep { defined && $_ ne '' } split m{/+}, $entry;
return File::Spec->catfile(
$paths->cache_root,
'open-file',
'java-sources',
$digest,
@parts,
);
}
lib/Developer/Dashboard/SkillDispatcher.pm view on Meta::CPAN
# Serves isolated skill browser routes and the older /skill bookmark namespace.
# Input: skill name, route path, and optional web app for rendering.
# Output: array reference HTTP response.
sub route_response {
my ( $self, %args ) = @_;
my $skill_name = $args{skill_name} || '';
my $route = defined $args{route} ? $args{route} : '';
my @skill_layers = $self->_skill_layers($skill_name);
return [ 404, 'text/plain; charset=utf-8', "Skill '$skill_name' not found\n" ] if !@skill_layers;
my @parts = grep { defined && $_ ne '' } split m{/+}, $route;
my @dashboards_roots = map { File::Spec->catdir( $_, 'dashboards' ) } @skill_layers;
return [ 404, 'text/plain; charset=utf-8', "Skill '$skill_name' does not provide dashboards\n" ]
if !grep { -d $_ } @dashboards_roots;
if ( @parts && $parts[0] eq 'bookmarks' ) {
if ( @parts == 1 ) {
my @items = $self->_skill_bookmark_entries($skill_name);
return [ 404, 'text/plain; charset=utf-8', "Skill '$skill_name' does not provide dashboards\n" ] if !@items;
return [ 200, 'application/json; charset=utf-8', encode_json( { skill => $skill_name, bookmarks => \@items } ) ];
}
lib/Developer/Dashboard/SkillDispatcher.pm view on Meta::CPAN
);
}
# _skill_layers($skill_name)
# Returns the participating installed roots for one skill in inheritance order.
# Input: skill repository name string and optional include_disabled flag.
# Output: ordered list of skill root directory path strings from home to leaf.
sub _skill_layers {
my ( $self, $skill_name, %args ) = @_;
return () if !$skill_name;
my @segments = grep { defined && $_ ne '' } split m{/+}, $skill_name;
return () if !@segments;
my $root_skill = shift @segments;
my $paths = $self->{manager}{paths};
my @layers = $paths->can('skill_layers')
? $paths->skill_layers( $root_skill, %args )
: do {
my $skill_path = $self->{manager}->get_skill_path( $root_skill, %args ) or return ();
($skill_path);
};
return @layers if !@segments;
lib/Developer/Dashboard/SkillDispatcher.pm view on Meta::CPAN
}
# _page_location($skill_name, $route_id)
# Resolves one skill dashboard file across every participating skill layer.
# Input: skill repository name string and route id string such as index or nav/foo.tt.
# Output: file path string and the skill layer root that provided it.
sub _page_location {
my ( $self, $skill_name, $route_id ) = @_;
return if !$skill_name || !$route_id;
for my $skill_path ( $self->_skill_lookup_roots($skill_name) ) {
my $file = File::Spec->catfile( $skill_path, 'dashboards', split m{/+}, $route_id );
return ( $file, $skill_path ) if -f $file;
}
return;
}
# skill_route_spec($kind, $skill_name, $target)
# Resolves one custom config/routes.json route definition for a skill route kind.
# Input: route kind string, skill repository name string, and relative route target path.
# Output: normalized route spec hash reference or undef when no custom route exists.
sub skill_route_spec {
lib/Developer/Dashboard/SkillDispatcher.pm view on Meta::CPAN
}
# skill_ajax_file_path($skill_name, $ajax_file)
# Resolves one layered skill-local dashboards/ajax file in deepest-first order.
# Input: skill repository name string and relative ajax file path.
# Output: absolute file path string or undef when missing.
sub skill_ajax_file_path {
my ( $self, $skill_name, $ajax_file ) = @_;
return if !$skill_name || !$ajax_file;
for my $skill_path ( $self->_skill_lookup_roots($skill_name) ) {
my $file = File::Spec->catfile( $skill_path, 'dashboards', 'ajax', split m{/+}, $ajax_file );
return $file if -f $file;
}
return;
}
# skill_static_file_path($skill_name, $type, $file)
# Resolves one layered skill-local dashboards/public asset in deepest-first order.
# Input: skill repository name string, static asset type, and relative file path.
# Output: absolute file path string or undef when missing.
sub skill_static_file_path {
my ( $self, $skill_name, $type, $file ) = @_;
return if !$skill_name || !$type || !$file;
for my $skill_path ( $self->_skill_lookup_roots($skill_name) ) {
my $candidate = File::Spec->catfile( $skill_path, 'dashboards', 'public', $type, split m{/+}, $file );
return $candidate if -f $candidate;
}
return;
}
# _skill_bookmark_entries($skill_name)
# Enumerates non-nav bookmark files contributed by one layered skill with deepest
# duplicates overriding shallower layers.
# Input: skill repository name string.
# Output: sorted list of bookmark entry names.
lib/Developer/Dashboard/Web/App.pm view on Meta::CPAN
# prefixed_ajax_file_response(%args)
# Resolves one `/ajax/...` request by preferring the longest matching skill prefix
# and falling back to the normal nested saved-ajax file path when no skill-local
# handler exists.
# Input: ajax_path plus normalized request metadata.
# Output: response array reference.
sub prefixed_ajax_file_response {
my ( $self, %args ) = @_;
my $ajax_path = $args{ajax_path} || '';
my @segments = grep { defined && $_ ne '' } split m{/+}, $ajax_path;
if ( my $spec = $self->_resolve_skill_route_spec(@segments) ) {
my $ajax_file = join '/', @{ $spec->{route_segments} || [] };
if ( $ajax_file ne '' ) {
my $saved_ajax_path = $self->_skill_ajax_file_path( $spec->{skill_name}, $ajax_file );
my $route_spec = $self->_skill_ajax_route_spec( $spec->{skill_name}, $ajax_file );
return $self->skill_ajax_file_response(
%args,
default_type => $route_spec ? ( $route_spec->{type} || '' ) : '',
skill_name => $spec->{skill_name},
ajax_file => $ajax_file,
lib/Developer/Dashboard/Web/App.pm view on Meta::CPAN
# prefixed_static_file_response(%args)
# Resolves one `/js/...`, `/css/...`, or `/others/...` request by preferring the
# longest matching skill prefix and falling back to the normal nested public
# asset path when no skill-local asset exists.
# Input: asset type, requested file path, and normalized request metadata.
# Output: response array reference.
sub prefixed_static_file_response {
my ( $self, %args ) = @_;
my $type = $args{type} || '';
my $file = $args{file} || '';
my @segments = grep { defined && $_ ne '' } split m{/+}, $file;
if ( my $spec = $self->_resolve_skill_route_spec(@segments) ) {
my $skill_file = join '/', @{ $spec->{route_segments} || [] };
if ( $skill_file ne '' ) {
my $resolved = $self->_skill_static_file_path( $spec->{skill_name}, $type, $skill_file );
my $route_spec = $self->_skill_route_spec( $type, $spec->{skill_name}, $skill_file );
return $self->skill_static_file_response(
%args,
default_type => $route_spec ? ( $route_spec->{type} || '' ) : '',
skill_name => $spec->{skill_name},
file => $skill_file,
lib/Developer/Dashboard/Web/App.pm view on Meta::CPAN
);
}
# _skill_app_fallback_response(%args)
# Attempts the /app/<skill> or /app/<skill>/<page> fallback before the blank saved-page editor opens.
# Input: unresolved app id plus normalized request metadata.
# Output: response array reference for a matched skill route, a 404 for missing namespaced skill routes, or undef when the id is not a skill route.
sub _skill_app_fallback_response {
my ( $self, %args ) = @_;
my $id = $args{id} || return;
my @segments = grep { defined && $_ ne '' } split m{/+}, $id;
return if !@segments;
require Developer::Dashboard::SkillDispatcher;
require Developer::Dashboard::SkillManager;
my $dispatcher = Developer::Dashboard::SkillDispatcher->new( paths => $self->{pages} ? $self->{pages}{paths} : undef );
my $manager = Developer::Dashboard::SkillManager->new( paths => $self->{pages} ? $self->{pages}{paths} : undef );
my $installed_skill = $manager->get_skill_path( $segments[0], include_disabled => 1 );
return $segments[1] ? [ 404, 'text/plain; charset=utf-8', "Not found\n" ] : undef if !$installed_skill;
my $spec = $dispatcher->resolve_route_segments( \@segments, include_disabled => 1 );
return $segments[1] ? [ 404, 'text/plain; charset=utf-8', "Not found\n" ] : undef if !$spec;
lib/Developer/Dashboard/Zipper.pm view on Meta::CPAN
return $dispatcher->skill_ajax_route_spec( $skill_name, $args{file} || '' );
}
# _url_path_escape($path)
# Escapes one slash-delimited route fragment without collapsing path separators.
# Input: relative path string.
# Output: URL-safe path string with each segment escaped independently.
sub _url_path_escape {
my ($path) = @_;
return '' if !defined $path || $path eq '';
return join '/', map { uri_escape($_) } split m{/+}, $path;
}
# _saved_ajax_url_and_store(%args)
# Stores saved bookmark Ajax code under the dashboards ajax tree and returns the stable runtime URL.
# Input: runtime_root, file, type, code, and optional base_url/singleton values.
# Output: hash reference with url and file path.
sub _saved_ajax_url_and_store {
my (%args) = @_;
my $path = saved_ajax_file_path(%args);
my $dir = dirname($path);
t/34-scorecard-guardrails.t view on Meta::CPAN
sub _git_tracks {
my ($path) = @_;
my ( $stdout, $stderr, $exit ) = capture {
system( 'git', '-C', $ROOT, 'ls-files', '--error-unmatch', $path );
};
return $exit == 0;
}
sub _slurp {
my ($relative_path) = @_;
my $path = File::Spec->catfile( $ROOT, split m{/}, $relative_path );
open my $fh, '<:raw', $path or die "Unable to read $path: $!";
local $/;
my $text = <$fh>;
close $fh or die "Unable to close $path: $!";
return $text;
}
__END__
=pod
t/47-owasp-gate.t view on Meta::CPAN
qr/\bcolumn_info\b/,
)
{
unlike( $source_code, $forbidden, "repo code body avoids forbidden security pattern: $forbidden" );
}
done_testing();
sub _slurp_repo {
my ($relative_path) = @_;
my $path = File::Spec->catfile( $ROOT, split m{/}, $relative_path );
open my $fh, '<:raw', $path or die "Unable to read $path: $!";
local $/;
my $text = <$fh>;
close $fh or die "Unable to close $path: $!";
return $text;
}
sub _code_only_repo_sources {
my @paths;