Communitygithub.com

perl-security

Guide complet de sécurité Perl couvrant le mode taint, la validation des entrées, l'exécution sécurisée de processus, les requêtes DBI paramétrées, la sécurité web (XSS/SQLi/CSRF) et les politiques de sécurité perlcritic. À activer lors de la révision du traitement des entrées Perl, de l'exécution de processus, des requêtes DBI ou du code exposé au web.

Qu'est-ce que perl-security ?

Cette compétence fournit un cadre complet de sécurité pour les applications Perl, ciblant les développeurs et les réviseurs de code. Elle couvre l'activation du mode taint, les modèles de validation d'entrée par liste blanche, la prévention des injections SQL avec des requêtes DBI paramétrées, l'exécution sécurisée de processus via la forme liste de `system`, et la protection contre les failles web courantes (XSS, CSRF). Elle intègre également des politiques de sécurité pour perlcritic afin d'automatiser la détection des mauvaises pratiques comme les `open` à deux arguments ou les `system` sous forme chaîne. La compétence est applicable à tout projet Perl, notamment ceux utilisant des frameworks web (CGI, Mojolicious, Dancer2) ou manipulant des entrées utilisateur, des chemins de fichiers ou des requêtes SQL. Elle met l'accent sur des exemples de code sûrs et dangereux, une liste de vérification rapide et des anti-patrons à éviter.

Compatible avec~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/affaan-m/ECC/tree/main/skills/perl-security

Demander à votre IA préférée

Ouvre une nouvelle conversation avec cette compétence d'agent déjà préchargée.

Documentation

Modèles de Sécurité Perl

Directives de sécurité complètes pour les applications Perl, couvrant la validation des entrées, la prévention des injections et les pratiques de codage sécurisé.

Quand Activer

  • Traitement des entrées utilisateur dans les applications Perl
  • Construction d'applications web Perl (CGI, Mojolicious, Dancer2, Catalyst)
  • Révision de code Perl pour des vulnérabilités de sécurité
  • Opérations sur fichiers avec des chemins fournis par l'utilisateur
  • Exécution de commandes système depuis Perl
  • Écriture de requêtes de base de données DBI

Comment Ça Fonctionne

Commencez par les limites d'entrée sensibles au mode taint, puis élargissez : validez et décontaminez les entrées, maintenez le système de fichiers et l'exécution de processus sous contrôle, et utilisez des requêtes DBI paramétrées partout. Les exemples ci-dessous montrent les valeurs par défaut sécurisées que cette compétence s'attend à voir appliquées avant de déployer du code Perl qui touche aux entrées utilisateur, au shell ou au réseau.

Mode Taint

Le mode taint de Perl (-T) suit les données provenant de sources externes et empêche leur utilisation dans des opérations dangereuses sans validation explicite.

Activation du Mode Taint

#!/usr/bin/perl -T
use v5.36;

# Tainté : tout ce qui vient de l'extérieur du programme
my $input    = $ARGV[0];        # Tainté
my $env_path = $ENV{PATH};      # Tainté
my $form     = <STDIN>;         # Tainté
my $query    = $ENV{QUERY_STRING}; # Tainté

# Nettoyer PATH dès le début (obligatoire en mode taint)
$ENV{PATH} = '/usr/local/bin:/usr/bin:/bin';
delete @ENV{qw(IFS CDPATH ENV BASH_ENV)};

Modèle de Décontamination

use v5.36;

# Bon : Valider et décontaminer avec une expression régulière spécifique
sub untaint_username($input) {
    if ($input =~ /^([a-zA-Z0-9_]{3,30})$/) {
        return $1;  # $1 est décontaminé
    }
    die "Nom d'utilisateur invalide : doit comporter 3 à 30 caractères alphanumériques\n";
}

# Bon : Valider et décontaminer un chemin de fichier
sub untaint_filename($input) {
    if ($input =~ m{^([a-zA-Z0-9._-]+)$}) {
        return $1;
    }
    die "Nom de fichier invalide : contient des caractères dangereux\n";
}

# Mauvais : Décontamination trop permissive (va à l'encontre du but)
sub bad_untaint($input) {
    $input =~ /^(.*)$/s;
    return $1;  # Accepte N'IMPORTE QUOI — inutile
}

Validation des Entrées

Liste Blanche Plutôt que Liste Noire

use v5.36;

# Bon : Liste blanche — définir exactement ce qui est autorisé
sub validate_sort_field($field) {
    my %allowed = map { $_ => 1 } qw(name email created_at updated_at);
    die "Champ de tri invalide : $field\n" unless $allowed{$field};
    return $field;
}

# Bon : Valider avec des modèles spécifiques
sub validate_email($email) {
    if ($email =~ /^([a-zA-Z0-9._%+-]+\@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})$/) {
        return $1;
    }
    die "Adresse e-mail invalide\n";
}

sub validate_integer($input) {
    if ($input =~ /^(-?\d{1,10})$/) {
        return $1 + 0;  # Forcer en nombre
    }
    die "Entier invalide\n";
}

# Mauvais : Liste noire — toujours incomplète
sub bad_validate($input) {
    die "Invalide" if $input =~ /[<>"';&|]/;  # Manque les attaques encodées
    return $input;
}

Contraintes de Longueur

use v5.36;

sub validate_comment($text) {
    die "Le commentaire est obligatoire\n"        unless length($text) > 0;
    die "Le commentaire dépasse 10000 caractères\n" if length($text) > 10_000;
    return $text;
}

Expressions Régulières Sûres

Prévention ReDoS

Le backtracking catastrophique se produit avec des quantificateurs imbriqués sur des motifs qui se chevauchent.

use v5.36;

# Mauvais : Vulnérable au ReDoS (backtracking exponentiel)
my $bad_re = qr/^(a+) + $/;           # Quantificateurs imbriqués
my $bad_re2 = qr/^([a-zA-Z]+)*$/;   # Quantificateurs imbriqués sur classe
my $bad_re3 = qr/^(.*?,){10,}$/;    # Combinaison gourmande/paresseuse répétée

# Bon : Réécrire sans imbrication
my $good_re = qr/^a+$/;             # Quantificateur unique
my $good_re2 = qr/^[a-zA-Z]+$/;     # Quantificateur unique sur classe

# Bon : Utiliser des quantificateurs possessifs ou des groupes atomiques pour empêcher le backtracking
my $safe_re = qr/^[a-zA-Z]++$/;             # Possessif (5.10+)
my $safe_re2 = qr/^(?>a+)$/;                # Groupe atomique

# Bon : Imposer un délai d'attente sur les motifs non fiables
use POSIX qw(alarm);
sub safe_match($string, $pattern, $timeout = 2) {
    my $matched;
    eval {
        local $SIG{ALRM} = sub { die "Délai d'attente de l'expression régulière dépassé\n" };
        alarm($timeout);
        $matched = $string =~ $pattern;
        alarm(0);
    };
    alarm(0);
    die $@ if $@;
    return $matched;
}

Opérations Fichiers Sûres

Ouverture à Trois Arguments

use v5.36;

# Bon : Ouverture à trois args, handle lexical, vérifier le retour
sub read_file($path) {
    open my $fh, '<:encoding(UTF-8)', $path
        or die "Impossible d'ouvrir '$path' : $!\n";
    local $/;
    my $content = <$fh>;
    close $fh;
    return $content;
}

# Mauvais : Ouverture à deux args avec des données utilisateur (injection de commande)
sub bad_read($path) {
    open my $fh, $path;        # Si $path = "|rm -rf /", exécute une commande !
    open my $fh, "< $path";   # Injection de métacaractères shell
}

Prévention TOCTOU et Traversée de Chemin

use v5.36;
use Fcntl qw(:DEFAULT :flock);
use File::Spec;
use Cwd qw(realpath);

# Création atomique de fichier
sub create_file_safe($path) {
    sysopen(my $fh, $path, O_WRONLY | O_CREAT | O_EXCL, 0600)
        or die "Impossible de créer '$path' : $!\n";
    return $fh;
}

# Valider que le chemin reste dans le répertoire autorisé
sub safe_path($base_dir, $user_path) {
    my $real = realpath(File::Spec->catfile($base_dir, $user_path))
        // die "Le chemin n'existe pas\n";
    my $base_real = realpath($base_dir)
        // die "Le répertoire de base n'existe pas\n";
    die "Traversée de chemin bloquée\n" unless $real =~ /^\Q$base_real\E(?:\/|\z)/;
    return $real;
}

Utilisez File::Temp pour les fichiers temporaires (tempfile(UNLINK => 1)) et flock(LOCK_EX) pour prévenir les conditions de concurrence.

Exécution de Processus Sûre

Forme Liste de system et exec

use v5.36;

# Bon : Forme liste — pas d'interpolation shell
sub run_command(@cmd) {
    system(@cmd) == 0
        or die "La commande a échoué : @cmd\n";
}

run_command('grep', '-r', $user_pattern, '/var/log/app/');

# Bon : Capturer la sortie de manière sûre avec IPC::Run3
use IPC::Run3;
sub capture_output(@cmd) {
    my ($stdout, $stderr);
    run3(\@cmd, \undef, \$stdout, \$stderr);
    if ($?) {
        die "La commande a échoué (exit $?) : $stderr\n";
    }
    return $stdout;
}

# Mauvais : Forme chaîne — injection shell !
sub bad_search($pattern) {
    system("grep -r '$pattern' /var/log/app/");  # Si $pattern = "'; rm -rf / #"
}

# Mauvais : Backticks avec interpolation
my $output = `ls $user_dir`;   # Risque d'injection shell

Utilisez aussi Capture::Tiny pour capturer stdout/stderr de commandes externes de manière sûre.

Prévention des Injections SQL

Placeholders DBI

use v5.36;
use DBI;

my $dbh = DBI->connect($dsn, $user, $pass, {
    RaiseError => 1,
    PrintError => 0,
    AutoCommit => 1,
});

# Bon : Requêtes paramétrées — toujours utiliser des placeholders
sub find_user($dbh, $email) {
    my $sth = $dbh->prepare('SELECT * FROM users WHERE email = ?');
    $sth->execute($email);
    return $sth->fetchrow_hashref;
}

sub search_users($dbh, $name, $status) {
    my $sth = $dbh->prepare(
        'SELECT * FROM users WHERE name LIKE ? AND status = ? ORDER BY name'
    );
    $sth->execute("%$name%", $status);
    return $sth->fetchall_arrayref({});
}

# Mauvais : Interpolation de chaîne en SQL (vulnérabilité SQLi !)
sub bad_find($dbh, $email) {
    my $sth = $dbh->prepare("SELECT * FROM users WHERE email = '$email'");
    # Si $email = "' OR 1=1 --", renvoie tous les utilisateurs
    $sth->execute;
    return $sth->fetchrow_hashref;
}

Listes Blanches de Colonnes Dynamiques

use v5.36;

# Bon : Valider les noms de colonnes contre une liste blanche
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 "Colonne invalide : $column\n"    unless $allowed_cols{$column};
    die "Direction invalide : $direction\n" unless $allowed_dirs{uc $direction};

    my $sth = $dbh->prepare("SELECT * FROM users ORDER BY $column $direction");
    $sth->execute;
    return $sth->fetchall_arrayref({});
}

# Mauvais : Interpolation directe d'une colonne choisie par l'utilisateur
sub bad_order($dbh, $column) {
    $dbh->prepare("SELECT * FROM users ORDER BY $column");  # SQLi !
}

DBIx::Class (ORM Sécurité)

use v5.36;

# DBIx::Class génère des requêtes paramétrées sûres
my @users = $schema->resultset('User')->search({
    status => 'active',
    email  => { -like => '%@example.com' },
}, {
    order_by => { -asc => 'name' },
    rows     => 50,
});

Sécurité Web

Prévention XSS

use v5.36;
use HTML::Entities qw(encode_entities);
use URI::Escape qw(uri_escape_utf8);

# Bon : Encoder la sortie pour le contexte HTML
sub safe_html($user_input) {
    return encode_entities($user_input);
}

# Bon : Encoder pour le contexte URL
sub safe_url_param($value) {
    return uri_escape_utf8($value);
}

# Bon : Encoder pour le contexte JSON
use JSON::MaybeXS qw(encode_json);
sub safe_json($data) {
    return encode_json($data);  # Gère l'échappement
}

# Échappement automatique des templates (Mojolicious)
# <%= $user_input %>   — échappé automatiquement (sûr)
# <%== $raw_html %>    — sortie brute (dangereux, utiliser uniquement pour du contenu de confiance)

# Échappement automatique des templates (Template Toolkit)
# [% user_input | html %]  — encodage HTML explicite

# Mauvais : Sortie brute en HTML
sub bad_html($input) {
    print "<div>$input</div>";  # XSS si $input contient <script>
}

Protection CSRF

use v5.36;
use Crypt::URandom qw(urandom);
use MIME::Base64 qw(encode_base64url);

sub generate_csrf_token() {
    return encode_base64url(urandom(32));
}

Utilisez une comparaison en temps constant lors de la vérification des jetons. La plupart des frameworks web (Mojolicious, Dancer2, Catalyst) fournissent une protection CSRF intégrée — préférez ceux-ci aux solutions faites maison.

Sécurité des Sessions et des En-têtes

use v5.36;

# Mojolicious sessions + en-têtes
$app->secrets(['long-random-secret-rotated-regularly']);
$app->sessions->secure(1);          # HTTPS uniquement
$app->sessions->samesite('Lax');

$app->hook(after_dispatch => sub ($c) {
    $c->res->headers->header('X-Content-Type-Options' => 'nosniff');
    $c->res->headers->header('X-Frame-Options'        => 'DENY');
    $c->res->headers->header('Content-Security-Policy' => "default-src 'self'");
    $c->res->headers->header('Strict-Transport-Security' => 'max-age=31536000; includeSubDomains');
});

Encodage de Sortie

Encodez toujours la sortie pour son contexte : HTML::Entities::encode_entities() pour le HTML, URI::Escape::uri_escape_utf8() pour les URL, JSON::MaybeXS::encode_json() pour le JSON.

Sécurité des Modules CPAN

  • Épingler les versions dans cpanfile : requires 'DBI', '== 1.643';
  • Privilégier les modules maintenus : Vérifier MetaCPAN pour les versions récentes
  • Minimiser les dépendances : Chaque dépendance est une surface d'attaque

Outils de Sécurité

Politiques de Sécurité perlcritic

# .perlcriticrc — configuration axée sur la sécurité
severity = 3
theme = security + core

# Exiger l'ouverture à trois arguments
[InputOutput::RequireThreeArgOpen]
severity = 5

# Exiger des appels système vérifiés
[InputOutput::RequireCheckedSyscalls]
functions = :builtins
severity = 4

# Interdire l'évaluation de chaîne
[BuiltinFunctions::ProhibitStringyEval]
severity = 5

# Interdire les opérateurs backtick
[InputOutput::ProhibitBacktickOperators]
severity = 4

# Exiger la vérification taint dans CGI
[Modules::RequireTaintChecking]
severity = 5

# Interdire l'ouverture à deux arguments
[InputOutput::ProhibitTwoArgOpen]
severity = 5

# Interdire les handles de fichier en mots nus
[InputOutput::ProhibitBarewordFileHandles]
severity = 5

Exécution de perlcritic

# Vérifier un fichier
perlcritic --severity 3 --theme security lib/MyApp/Handler.pm

# Vérifier l'ensemble du projet
perlcritic --severity 3 --theme security lib/

# Intégration CI
perlcritic --severity 4 --theme security --quiet lib/ || exit 1

Liste de Vérification Rapide

VérificationQue Vérifier
Mode taintDrapeau -T sur les scripts CGI/web
Validation des entréesModèles de liste blanche, limites de longueur
Opérations fichiersOuverture à trois arguments, vérification de traversée de chemin
Exécution de processusForme liste system, pas d'interpolation shell
Requêtes SQLPlaceholders DBI, ne jamais interpoler
Sortie HTMLencode_entities(), échappement automatique des templates
Jetons CSRFGénérés, vérifiés sur les requêtes modifiant l'état
Configuration sessionCookies Secure, HttpOnly, SameSite
En-têtes HTTPCSP, X-Frame-Options, HSTS
DépendancesVersions épinglées, modules audités
Sécurité des regexPas de quantificateurs imbriqués, motifs ancrés
Messages d'erreurAucune trace de pile ou chemin divulgué aux utilisateurs

Anti-Patrons

# 1. Ouverture à deux arguments avec des données utilisateur (injection de commande)
open my $fh, $user_input;               # Vulnérabilité CRITIQUE

# 2. system sous forme chaîne (injection shell)
system("convert $user_file output.png"); # Vulnérabilité CRITIQUE

# 3. Interpolation de chaîne SQL
$dbh->do("DELETE FROM users WHERE id = $id");  # SQLi

# 4. eval avec entrée utilisateur (injection de code)
eval $user_code;                         # Exécution de code à distance

# 5. Faire confiance à $ENV sans nettoyage
my $path = $ENV{UPLOAD_DIR};             # Pourrait être manipulé
system("ls $path");                      # Double vulnérabilité

# 6. Désactiver le mode taint sans validation
($input) = $input =~ /(.*)/s;           # Décontamination paresseuse — va à l'encontre du but

# 7. Données utilisateur brutes en HTML
print "<div>Welcome, $username!</div>";  # XSS

# 8. Redirections non validées
print $cgi->redirect($user_url);         # Redirection ouverte

Rappel : La flexibilité de Perl est puissante mais nécessite de la discipline. Utilisez le mode taint pour le code exposé au web, validez toutes les entrées avec des listes blanches, utilisez des placeholders DBI pour chaque requête, et encodez toute sortie pour son contexte. Défense en profondeur — ne comptez jamais sur une seule couche.

Individual skills in this repo

This repo contains 20 individual skills — each has its own dedicated page.

accessibility

Design, implement, and audit inclusive digital products using WCAG 2.2 Level AA. Use when building or auditing UI that must meet WCAG 2.2 Level AA, or when reviewing a change for keyboard, contrast, or screen-reader support.

affaan-m/content-engine

Create platform-native content systems for X, LinkedIn, TikTok, YouTube, newsletters, and repurposed multi-platform campaigns. Use when the user wants social posts, threads, scripts, content calendars, or one source asset adapted cleanly across platforms.

affaan-m/ECC

Multi-platform content distribution across X, LinkedIn, Threads, and Bluesky. Adapts content per platform using content-engine patterns. Never posts identical content cross-platform. Use when the user wants to distribute content across social platforms.

affaan-m/fal-ai-media

Unified media generation via fal.ai MCP — image, video, and audio. Covers text-to-image (Nano Banana), text/image-to-video (Seedance, Kling, Veo 3), text-to-speech (CSM-1B), and video-to-audio (ThinkSound). Use when the user wants to generate images, videos, or audio with AI.

affaan-m/manim-video

日本語翻訳:このファイルは manim-video 用の日本語翻訳が必要です

affaan-m/remotion-video-creation

Remotion のベストプラクティス - React で動画を作成する。3D、アニメーション、音声、字幕、チャート、トランジションなどをカバーするドメイン固有の29のルール。

affaan-m/video-editing

AI-assisted video editing workflows for cutting, structuring, and augmenting real footage. Covers the full pipeline from raw capture through FFmpeg, Remotion, ElevenLabs, fal.ai, and final polish in Descript or CapCut. Use when the user wants to edit video, cut footage, create vlogs, or build video content.

agent-architecture-audit

Full-stack diagnostic for agent and LLM applications. Audits the 12-layer agent stack for wrapper regression, memory pollution, tool discipline failures, hidden repair loops, and rendering corruption. Produces severity-ranked findings with code-first fixes. Essential for developers building agent applications, autonomous loops, or any LLM-powered feature. Use when an agent or LLM feature misbehaves and the failing layer is unknown, or before shipping an agent stack.

agent-eval

Head-to-head comparison of coding agents (Claude Code, Aider, Codex, etc.) on custom tasks with pass rate, cost, time, and consistency metrics. Use when choosing between coding agents, or when a change to an agent setup needs measured pass rate, cost, and time rather than an impression.

agent-harness-construction

Design and optimize AI agent action spaces, tool definitions, and observation formatting for higher completion rates. Use when defining or revising an agent

agentic-engineering

Operate as an agentic engineer using eval-first execution, decomposition, and cost-aware model routing. Use when planning or executing engineering work that agents will carry out end to end.

agentic-os

Build persistent multi-agent operating systems on Claude Code. Covers kernel architecture, specialist agents, slash commands, file-based memory, scheduled automation, and state management without external databases. Use when building a persistent multi-agent system on Claude Code with its own memory, commands, and scheduling.

agent-introspection-debugging

Structured self-debugging workflow for AI agent failures using capture, diagnosis, contained recovery, and introspection reports. Use when an agent run fails and you need a reproducible diagnosis instead of a retry.

agent-payment-x402

Add x402 payment execution to AI agents with per-task budgets, spending controls, and non-custodial wallets. Supports Base through agentwallet-sdk and X Layer through OKX Payments / OKX Agent Payments Protocol. Use when an agent must pay for something itself and needs per-task budgets, spending controls, and a non-custodial wallet.

agent-self-evaluation

Use after completing any non-trivial task. The agent self-rates its output on 5 axes — accuracy, completeness, clarity, actionability, conciseness — with concrete evidence per criterion. Produces a structured 1-5 scorecard with specific improvement suggestions.

agent-sort

Build an evidence-backed ECC install plan for a specific repo by sorting skills, commands, rules, hooks, and extras into DAILY vs LIBRARY buckets using parallel repo-aware review passes. Use when ECC should be trimmed to what a project actually needs instead of loading the full bundle.

ai-first-engineering

Engineering operating model for teams where AI agents generate a large share of implementation output. Use when setting team process, review gates, or ownership rules for a codebase largely written by agents.

ai-regression-testing

Regression testing strategies for AI-assisted development. Sandbox-mode API testing without database dependencies, automated bug-check workflows, and patterns to catch AI blind spots where the same model writes and reviews code. Use when adding regression coverage to AI-assisted code, or when the same model both wrote and reviewed a change.

android-clean-architecture

Clean Architecture patterns for Android and Kotlin Multiplatform projects — module structure, dependency rules, UseCases, Repositories, and data layer patterns. Use when structuring modules, layers, or data flow in an Android or KMP project.

angular-developer

Generates Angular code and provides architectural guidance. Trigger when creating projects, components, or services, or for best practices on reactivity (signals, linkedSignal, resource), forms, dependency injection, routing, SSR, accessibility (ARIA), animations, styling (component styles, Tailwind CSS), testing, or CLI tooling.

Skills associés