Perl Security Skill

100 0 Updated: 2026-07-15 15:15:37

The Perl Security Skill is a specialized skill within the ECC (Agent Harness Performance Optimization System), focusing on secure development and vulnerability protection for the Perl programming language. It provides core features such as Perl code security best practices, common vulnerability detection and remediation, input validation, output encoding, SQL injection protection, and XSS protection. Suitable for developers writing secure Perl scripts, security auditors, and teams looking to enhance the security of their Perl applications. By integrating this skill, you can automatically detect security risks in Perl code and receive remediation suggestions, ensuring the security of Perl applications in production environments.

Install
bunx skills add https://github.com/affaan-m/ECC.git --skill perl-security
Skill Details readonly

I. Skill Overview
This skill is applicable to scenarios such as processing user input, building Perl web applications (CGI, Mojolicious, Dancer2, Catalyst), reviewing Perl code for security vulnerabilities, performing file path operations supplied by users, executing system commands from Perl, and writing DBI database queries.

Core principle: Start from the Taint‑aware input boundary and work outward – validate and untaint inputs, constrain filesystem and process execution, and always use parameterised DBI queries.

II. Core Functions
1. Taint Mode
Perl's -T taint mode tracks data originating from outside and prevents it from being used in unsafe operations without explicit validation.

Enable taint mode:

perl
#!/usr/bin/perl -T
use v5.36;
# All data from external sources is tainted
my $input = $ARGV[0];        # tainted
my $env_path = $ENV{PATH};   # tainted

Untainting: Validate with a specific regex and capture the matched part – $1 is untainted.

perl
# Good: validate and untaint
sub untaint_username($input) {
    if ($input =~ /^([a-zA-Z0-9_]{3,30})$/) {
        return $1;  # $1 is untainted
    }
    die "Invalid username\n";
}

2. Input Validation: Whitelist over Blacklist
Use a whitelist to precisely define allowed content; blacklists always risk missing encoding attacks.

perl
# Good: whitelist
sub validate_sort_field($field) {
    my %allowed = map { $_ => 1 } qw(name email created_at updated_at);
    die "Invalid sort field: $field\n" unless $allowed{$field};
    return $field;
}

# Bad: blacklist (always incomplete)
sub bad_validate($input) {
    die "Invalid" if $input =~ /[<>"';&|]/;  # misses encoding attacks
    return $input;
}

3. ReDoS Protection
Nested quantifiers on overlapping patterns can cause catastrophic backtracking.

perl
# Bad: vulnerable to ReDoS
my $bad_re = qr/^(a+)+$/;        # nested quantifier
my $bad_re2 = qr/^([a-zA-Z]+)*$/; # nested quantifier

# Good: no nesting
my $good_re = qr/^a+$/;          # single quantifier

# Good: use possessive quantifiers or atomic groups
my $safe_re = qr/^[a-zA-Z]++$/;  # possessive quantifier (5.10+)
my $safe_re2 = qr/^(?>a+)$/;     # atomic group

4. Safe File Operations
Three‑argument open: use the three‑argument form, lexical filehandles, and check return values.

perl
# Good: three‑argument open
sub read_file($path) {
    open my $fh, '<:encoding(UTF-8)', $path or die "Cannot open '$path': $!\n";
    local $/;
    my $content = <$fh>;
    close $fh;
    return $content;
}

# Bad: two‑argument open (command injection risk)
sub bad_read($path) {
    open my $fh, $path;  # if $path = "|rm -rf /", this executes a command!
}

Path traversal protection: verify that the path always stays within the allowed directory.

perl
sub safe_path($base_dir, $user_path) {
    my $real = realpath(File::Spec->catfile($base_dir, $user_path))
        // die "Path does not exist\n";
    my $base_real = realpath($base_dir) // die "Base dir does not exist\n";
    die "Path traversal blocked\n" unless $real =~ /^\Q$base_real\E(?:\/|\z)/;
    return $real;
}

5. Safe Process Execution
List form of system/exec: bypasses the shell, no command injection risk.

perl
# Good: list form – no shell interpolation
sub run_command(@cmd) {
    system(@cmd) == 0 or die "Command failed: @cmd\n";
}
run_command('grep', '-r', $user_pattern, '/var/log/app/');

# Bad: string form – shell injection!
sub bad_search($pattern) {
    system("grep -r '$pattern' /var/log/app/");
    # if $pattern = "'; rm -rf / #"
}

6. SQL Injection Protection
DBI placeholders: always use parameterised queries.

perl
# Good: parameterised query – always use placeholders
sub find_user($dbh, $email) {
    my $sth = $dbh->prepare('SELECT * FROM users WHERE email = ?');
    $sth->execute($email);
    return $sth->fetchrow_hashref;
}

# Bad: string interpolation – SQL injection!
sub bad_find($dbh, $email) {
    my $sth = $dbh->prepare("SELECT * FROM users WHERE email = '$email'");
    # if $email = "' OR 1=1 --", returns all users
    $sth->execute;
    return $sth->fetchrow_hashref;
}

Dynamic column name whitelist: whitelist user‑selected column names.

perl
sub order_by($dbh, $column, $direction) {
    my %allowed_cols = map { $_ => 1 } qw(name email created_at);
    my %allowed_dirs = map { $_ => 1 } qw(ASC DESC);
    die "Invalid column: $column\n" unless $allowed_cols{$column};
    die "Invalid direction: $direction\n" unless $allowed_dirs{uc $direction};
    my $sth = $dbh->prepare("SELECT * FROM users ORDER BY $column $direction");
    $sth->execute;
    return $sth->fetchall_arrayref({});
}

7. Web Security (XSS Protection)
Encode output according to context.

perl
use HTML::Entities qw(encode_entities);
use URI::Escape qw(uri_escape_utf8);

# HTML context encoding
sub safe_html($user_input) {
    return encode_entities($user_input);
}

# URL context encoding
sub safe_url_param($value) {
    return uri_escape_utf8($value);
}

Template auto‑escaping:

  • Mojolicious: <%= $user_input %> – auto‑escaped (safe); <%== $raw_html %> – raw output (dangerous, only for trusted content)

  • Template Toolkit: [% user_input | html %] – explicit HTML encoding

III. Primary Use Cases

  • Secure Perl application development – security baseline when writing web applications in CGI, Mojolicious, Dancer2, Catalyst, etc.

  • Code security reviews – review Perl code for injection, file operation, and command execution vulnerabilities

  • Database query security – ensure DBI queries use parameterisation to prevent SQL injection

  • Secure system command execution – use the list form of system/exec to avoid shell injection

  • Secure file operations – three‑argument open, path traversal protection, TOCTOU protection

  • Regular expression security – prevent ReDoS attacks

  • Enable Taint Mode – use the -T flag to track external data flow

IV. Important Principles

  • Enable Taint Mode – use #!/usr/bin/perl -T to track external data

  • Whitelist validation – precisely define allowed values rather than trying to block bad ones

  • Parameterised queries – always use placeholders for DBI queries, never concatenate strings

  • List‑form system – use system(@cmd) instead of system("$cmd $arg")

  • Three‑argument open – use open my $fh, '<', $path rather than the two‑argument form

  • Path validation – use realpath to verify that paths do not escape the allowed directory

  • ReDoS protection – avoid nested quantifiers, use possessive quantifiers or atomic groups

  • Output encoding – encode output correctly according to HTML/URL/JSON context

V. Summary
Perl Security is a comprehensive set of Perl application security best practices, covering seven core security areas: Taint Mode, input validation, ReDoS protection, safe file operations, safe process execution, DBI parameterised queries to prevent SQL injection, and web security (XSS). It provides clear “incorrect vs correct” comparative examples and immediately usable code templates, suitable for Perl developers in scenarios such as new project security baselining, code auditing, database query security, and secure system command execution.