carthage-software/mago
Mago is an extremely fast PHP linter, formatter, and static analyzer written in Rust. It brings Rust-inspired speed and reliability to PHP projects with a modern toolchain and great developer experience, plus multiple install options (script, Homebrew, Composer).
This document details the rules available in the Security category.
| Rule | Code |
|---|---|
| Disallowed Functions | disallowed-functions |
| No Database Schema Changes | no-db-schema-change |
| No Debug Symbols | no-debug-symbols |
| No Insecure Comparison | no-insecure-comparison |
| No Literal Password | no-literal-password |
| No Roles As Capabilities | no-roles-as-capabilities |
| No Short Opening Tag | no-short-opening-tag |
| No Unescaped Output | no-unescaped-output |
Require preg_quote Delimiter |
require-preg-quote-delimiter |
| Sensitive Parameter | sensitive-parameter |
| Tainted Data to Sink | tainted-data-to-sink |
disallowed-functionsFlags calls to functions that are disallowed via rule configuration.
You can specify which functions or extensions should be disallowed through the
functions or extensions options. This helps enforce coding standards,
security restrictions, or the usage of preferred alternatives.
Each entry can be a simple string or an object with name and optional help:
functions = [
'eval',
{ name = 'error_log', help = 'Use MyLogger instead.' },
]
| Option | Type | Default |
|---|---|---|
enabled |
boolean |
true |
level |
string |
"warning" |
functions |
array |
[] |
extensions |
array |
[] |
<?php
function allowed_function(): void {
// ...
}
allowed_function(); // Not flagged
<?php
curl_init(); // Error: part of a disallowed extension
no-db-schema-changeThis rule flags any attempt to alter the database schema (using CREATE, ALTER, or DROP)
within a $wpdb call. Schema modifications must only occur within a plugin activation hook
to prevent catastrophic performance issues and data corruption.
WordPress| Option | Type | Default |
|---|---|---|
enabled |
boolean |
true |
level |
string |
"error" |
<?php
function my_plugin_activate() {
global $wpdb;
// Running schema changes inside an activation hook is safe.
$wpdb->query("ALTER TABLE {$wpdb->posts} ADD my_column VARCHAR(255)");
}
register_activation_hook(__FILE__, 'my_plugin_activate');
<?php
// This schema change runs on every page load, which is very dangerous.
global $wpdb;
$wpdb->query("ALTER TABLE {$wpdb->posts} ADD my_column VARCHAR(255)");
no-debug-symbolsFlags calls to debug functions like var_dump, print_r, dd, etc.
These functions are useful for debugging, but they should not be committed to version control as they can expose sensitive information and are generally not intended for production environments.
| Option | Type | Default |
|---|---|---|
enabled |
boolean |
true |
level |
string |
"note" |
<?php
// Production-safe code
error_log('Processing user request.');
<?php
function process_request(array $data) {
var_dump($data); // Debug call that should be removed
// ...
}
no-insecure-comparisonDetects insecure comparison of passwords or tokens using ==, !=, ===, or !==.
These operators are vulnerable to timing attacks, which can expose sensitive information.
Instead, use hash_equals for comparing strings or password_verify for validating hashes.
| Option | Type | Default |
|---|---|---|
enabled |
boolean |
true |
level |
string |
"error" |
<?php
if (hash_equals($storedToken, $userToken)) {
// Valid token
}
<?php
if ($storedToken == $userToken) {
// Vulnerable to timing attacks
}
no-literal-passwordDetects the use of literal values for passwords or sensitive data. Storing passwords or sensitive information as literals in code is a security risk and should be avoided. Use environment variables or secure configuration management instead.
| Option | Type | Default |
|---|---|---|
enabled |
boolean |
true |
level |
string |
"error" |
<?php
$password = getenv('DB_PASSWORD');
<?php
$password = "supersecret";
no-roles-as-capabilitiesThis rule flags the use of user roles (e.g., 'administrator') in functions that expect a
granular capability (e.g., 'edit_posts'). Checking against specific capabilities is a
core security principle in WordPress.
WordPress| Option | Type | Default |
|---|---|---|
enabled |
boolean |
true |
level |
string |
"warning" |
<?php
if ( current_user_can( 'edit_posts' ) ) { /* ... */ }
<?php
// This check is brittle and will fail if roles are customized.
if ( current_user_can( 'editor' ) ) { /* ... */ }
no-short-opening-tagDisallows the use of short opening tags (<?).
The availability of <? depends on the short_open_tag directive in php.ini. If
this setting is disabled on a server, any code within the short tags will be
exposed as plain text, which is a significant security risk. Using the full <?php
opening tag is the only guaranteed portable way to ensure your code is always
interpreted correctly.
| Option | Type | Default |
|---|---|---|
enabled |
boolean |
true |
level |
string |
"warning" |
<?php
echo "Hello, World!";
<?
echo "Hello, World!";
no-unescaped-outputThis rule ensures that any variable or function call that is output directly to the page is properly escaped. All data must be escaped before printing to prevent Cross-Site Scripting (XSS) vulnerabilities.
WordPress| Option | Type | Default |
|---|---|---|
enabled |
boolean |
true |
level |
string |
"error" |
<?php
echo esc_html( $user_comment );
?>
<a href="<?php echo esc_url( $user_provided_url ); ?>">Link</a>
<?php
// This is a major XSS vulnerability.
echo $_GET['user_comment'];
require-preg-quote-delimiterThis rule requires that when using preg_quote(), the second $delimiter argument is always provided.
If the string being quoted contains the same character used for your regex delimiter (e.g., /),
failing to provide the second argument will prevent that character from being escaped,
which can break the regular expression.
| Option | Type | Default |
|---|---|---|
enabled |
boolean |
true |
level |
string |
"warning" |
<?php
// The delimiter is provided, ensuring it gets escaped if necessary.
$pattern = '/' . preg_quote( $user_input, '/' ) . '/';
<?php
// If $user_input contains '/', the regex will be invalid.
$pattern = '/' . preg_quote( $user_input ) . '/';
sensitive-parameterRequires that parameters that are likely to contain sensitive information (e.g., passwords)
are marked with the #[SensitiveParameter] attribute to prevent accidental logging or exposure.
This rule only applies to PHP 8.2 and later, as the SensitiveParameter attribute was introduced in PHP 8.2.
8.2.0| Option | Type | Default |
|---|---|---|
enabled |
boolean |
true |
level |
string |
"error" |
<?php
function login(string $username, #[SensitiveParameter] string $password): void {
// ...
}
<?php
function login(string $username, string $password): void {
// ...
}
tainted-data-to-sinkDetects user (tainted) data being passed directly to sink functions or constructs
(such as echo, print, or user-defined "log" functions). If these functions emit
or store data without sanitization, it could lead to Cross-Site Scripting (XSS)
or other injection attacks.
| Option | Type | Default |
|---|---|---|
enabled |
boolean |
true |
level |
string |
"error" |
known-sink-functions |
array |
["printf"] |
<?php
// Properly escape data before using a sink like `echo`
echo htmlspecialchars($_GET['name'] ?? '', ENT_QUOTES, 'UTF-8');
<?php
// This is considered unsafe:
echo $_GET['name'] ?? '';
How can I help you explore Laravel packages today?