mcpField
D
drupal-security

drupal-security

github.com/edutrulSecurity7349 installsUpdated 21w ago
Repository

About drupal-security

A security coding reference for Drupal developers, focused on common vulnerability patterns and safeguards. It covers database queries, output escaping, permissions, CSRF, file uploads, and sensitive data handling.

Key features

  1. 01Prevent SQL injection and XSS
  2. 02Check access permissions and CSRF
  3. 03Validate uploaded files
  4. 04Avoid exposing sensitive data
  5. 05Spot common dangerous code patterns

Use cases

  1. 01Review Drupal module code
  2. 02Implement secure custom endpoints
  3. 03Check file upload handling

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/edutrul/drupal-ai --skill drupal-security

After installing, type/drupal-securityin Claude Code to confirm it loaded.

Bundled filesREADME.md

Details

From the project README

Drupal Security

Critical Security Patterns

SQL Injection Prevention

NEVER concatenate user input into queries:

// VULNERABLE - SQL injection
$query = "SELECT * FROM users WHERE name = '" . $name . "'";
$result = $connection->query($query);

// SAFE - parameterized query
$result = $connection->select('users', 'u')
  ->fields('u')
  ->condition('name', $name)
  ->execute();

// SAFE - placeholder
$result = $connection->query(
  'SELECT * FROM {users} WHERE name = :name',
  [':name' => $name]
);
XSS Prevention

Always escape output. Trust the render system:

// VULNERABLE - raw HTML output
return ['#markup' => $user_input];
return ['#markup' => '<div>' . $title . '</div>'];

// SAFE - plain text (auto-escaped)
return ['#plain_text' => $user_input];

// SAFE - use proper render elements
return [
  '#type' => 'html_tag',
  '#tag' => 'div',
  '#value' => $title,  // Escaped automatically
];

// SAFE - Twig auto-escapes
{{ variable }}  // Escaped
{{ variable|raw }}  // DANGEROUS - only for trusted HTML

For admin-only content:

use Drupal\Component\Utility\Xss;

// Filter but allow safe HTML tags
$safe = Xss::filterAdmin($user_html);

Note: Never pass untrusted user input into #markup. Prefer #plain_text, Twig auto-escaping, or safe render elements.

Access Control

Always verify permissions:

// In routing.yml
my_module.admin:
  path: '/admin/my-module'
  requirements:
    _permission: 'administer my_module'  # Required!

// In code
if (!$this->currentUser->hasPermission('administer my_module')) {
  throw new AccessDeniedHttpException();
}

// Entity queries - check access!
$query = $this->entityTypeManager
  ->getStorage('node')
  ->getQuery()
  ->accessCheck(TRUE)  // CRITICAL - never FALSE unless intentional
  ->condition('type', 'article');
CSRF Protection

Forms automatically include CSRF tokens. For custom AJAX:

// Include token in AJAX requests
$build['#attached']['drupalSettings']['myModule']['token'] =
  \Drupal::csrfToken()->get('my_module_action');

Note: Prefer dependency injection over direct \Drupal::service() or \Drupal:: calls in classes.

// Validate in controller
if (!$this->csrfToken->validate($token, 'my_module_action')) {
  throw new AccessDeniedHttpException('Invalid token');
}
File Upload Security
$validators = [
  'file_validate_extensions' => ['pdf doc docx'],  // Whitelist extensions
  'file_validate_size' => [25600000],  // 25MB limit
  'FileSecurity' => [],  // Drupal 10.2+ - blocks dangerous files
];

// NEVER trust file extension alone - check MIME type
$file_mime = $file->getMimeType();
$allowed_mimes = ['application/pdf', 'application/msword'];
if (!in_array($file_mime, $allowed_mimes)) {
  // Reject file
}
Sensitive Data
// NEVER log sensitive data
$this->logger->info('User @user logged in', ['@user' => $username]);
// NOT: $this->logger->info('Login: ' . $username . ':' . $password);

// NEVER expose in error messages
throw new \Exception('Database error');  // Generic
// NOT: throw new \Exception('Query failed: ' . $query);

// Use environment variables for secrets
$api_key = getenv('MY_API_KEY');
// NOT: $api_key = 'hardcoded-secret-key';

Red Flags to Watch For

When you see these patterns, immediately warn:

PatternRiskFix
String concatenation in SQLSQL injectionUse query builder
#markup with variablesXSSUse #plain_text
accessCheck(FALSE)Access bypassUse accessCheck(TRUE)
Missing _permission in routesUnauthorized accessAdd permission
Custom controller with no access checkUnauthorized accessAdd route permission or access callback
{{ var|raw }} in TwigXSSRemove |raw
Hardcoded passwords/keysCredential exposureUse env vars
eval() or exec()Code injectionAvoid entirely
unserialize() on user dataObject injectionUse JSON
Missing CSRF validation in custom endpointsCSRF attackValidate token using csrf_token service
File upload without validationMalicious file uploadValidate extension, size, and MIME type
Direct \Drupal:: usage in classesHard to test / bad practiceUse dependency injection
Logging sensitive dataInformation disclosureMask or avoid logging sensitive fields

Comments

Markdown supported