Docs / Remediation guide
Every finding in your dashboard comes with a suggested fix. This guide explains each type of finding in plain English, why it matters, and the practical ways to fix it.
What it means: the site runs a version of Drupal core, a module, or a PHP library it depends on (Symfony, Guzzle, Twig and so on) that has a published security advisory, which makes it a known, documented way in. These are the findings to fix first.
"Confirmed" or "possible"? A confirmed finding means Drubix knows the exact installed version, from GitHub or the connector. A possible exposure means the module is there but its version couldn't be read from outside. Connect GitHub or install the connector to confirm it either way.
With the site's repository connected to GitHub, click Create fix PR on the finding. Drubix opens a pull request that updates composer.json and composer.lock to the fixed version, including every core package together for a core fix. When several sites are affected, use Fix on all N sites. To have patch-level fixes merged automatically after your CI passes, turn on Autopilot.
composer require drupal/webform:^6.2.12 --with-all-dependenciesA fix PR already includes the new
composer.lock.drush updatedb -y drush cr
When an update has to wait (a code freeze, a client sign-off, a big upgrade), click Temporary firewall rule on the finding. Drubix reads the module's routes from the exact release the site runs and gives you a rule that blocks the module's public URLs, for Cloudflare, Nginx or Apache. Admin pages and ordinary Drupal pages (nodes, users) are never in it.
matches need a Cloudflare plan that supports regular expressions; on other plans use the Nginx or Apache version.server { } block, above location /, then run nginx -t and reload..htaccess right after RewriteEngine on.It's a stopgap, not a fix. While the rule is on, visitors get a 403 on those URLs, so the module's public features (its forms, for example) stop working. The vulnerability may also be reachable some other way. Update as soon as you can, then remove the rule. There's no rule for Drupal core or PHP libraries, because their code runs on every page.
What it means: the site doesn't send some of the response headers that tell browsers to block common attacks, such as clickjacking, content-type tricks and insecure connections. It's the most common finding, and it only needs fixing in one place. Pick whichever matches your hosting:
If the site is behind a CDN such as Cloudflare or Fastly, this is usually easiest, and it works even when you can't change the server. In Cloudflare, use Rules → Transform Rules → Modify Response Header. No code changes are needed.
This applies to every request, whatever the application does. For Nginx:
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; add_header X-Frame-Options "SAMEORIGIN" always; add_header X-Content-Type-Options "nosniff" always; add_header Referrer-Policy "strict-origin-when-cross-origin" always; add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:;" always;
For Apache, in .htaccess or the vhost:
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains" Header always set X-Frame-Options "SAMEORIGIN" Header always set X-Content-Type-Options "nosniff" Header always set Referrer-Policy "strict-origin-when-cross-origin"
Install the Security Kit (seckit) module and configure it at /admin/config/system/seckit. This is useful when someone else manages the hosting.
Take Content-Security-Policy slowly. It's the header most likely to break a live site, by blocking inline scripts, tag managers or third-party widgets. Send it as Content-Security-Policy-Report-Only for a week or two, review what it would have blocked, and only then switch to the enforcing header.
What it means: files that should never be public can be downloaded from the site. This is usually caused by the deploy process copying too much, not by the code.
.git folder: anyone could download your source code and its history. Change the deploy step to leave .git/ out, or better, deploy from a build artifact that never contains it..env file: this can leak passwords and API keys. Remove it from the web folder and change any secrets it held.composer.lock: not secret in itself, but it tells attackers your exact versions.Also block hidden files and the lock file at the web server. For Nginx:
location ~ /\.(?!well-known) { deny all; }
location ~ /composer\.(json|lock)$ { deny all; }
What it means: nobody maintains the module any more. It may be safe today, but if a vulnerability is found, no fix will come. Options, best first:
What it means: a setting inside Drupal makes the site easier to attack. These findings need the connector module, because they can't be seen from outside.
/admin/people/permissions.settings.php, a missing .htaccess in the files folder, or errors shown on screen in production. The finding names the exact setting. Error display is set at /admin/config/development/logging.require-dev./admin/config/content/formats.$settings['trusted_host_patterns'] with your real domains to settings.php.What it means: something about who can administer the site needs a look. These findings need the connector module.
What it means: a password, API key, private key, .env file or database dump is committed to the site's GitHub repository. Anyone who can see the repo, now or later, can use it. These findings need GitHub connected.
getenv() in settings.php..env, *.sql and key files to .gitignore, and commit example files with placeholder values.What it means: the client's domain is easy to impersonate by email, or a subdomain points at a service that no longer exists.
~all or -all. A domain that never sends email should publish v=spf1 -all._dmarc. telling mail providers what to do with emails that fail checks. Start with p=none to watch the reports, then move to p=quarantine and p=reject.What it means: a pattern in the site's own modules, themes or templates that often leads to a vulnerability. Security advisories never cover custom code, so this is the only check it gets. These findings need GitHub connected, and each one is a lead for a developer to confirm, not a proven bug.
$connection->query('… WHERE nid = :nid', [':nid' => $nid]), or the query builder.#markup or Markup::create(): use #plain_text for text, or $this->t('@value', ['@value' => $value]), which escapes.|raw or autoescape off: remove it, or sanitise the value with Xss::filter() in a preprocess function._access: 'TRUE': use _permission unless the page is meant to be public, and add _csrf_token: 'TRUE' to routes that change data.unserialize(), eval() or shell calls on variables: use json_decode(), remove eval(), and pass shell arguments through escapeshellarg() or Symfony Process.innerHTML in JavaScript: use textContent, or sanitise with DOMPurify.If a flagged line is safe, add a drubix:ignore comment on it and the finding disappears on the next scan.
What it means: once a day the connector compares Drupal core and every contrib module and theme with the official release from drupal.org. These are the findings that most often mean a site has already been hacked.
Run the check on demand with drush drubix:integrity.
What it means: once a day Drubix opens the homepage and a few key pages (login, checkout, contact) in a real browser, the way a visitor would, and checks what actually loads. The first check records which outside services the site uses; later checks flag anything new.
.htaccess, settings.php, index.php and recently changed theme files.What it means: Drupal's AI agents, which can create fields, content types and configuration from plain-language instructions, were active around the same time something risky changed. For example, a dangerous permission was granted or configuration drifted. Most sites never see this finding. It needs the connector, and it appears only when Drupal's AI logging recorded agent activity.
It shows that two things happened together, not that the AI caused the change, and the finding always says so. Treat it as a reason to check:
/admin/reports/dblog and filter by the time shown. Drupal's log records which user or service account did what.Contact us with the finding and what you've tried. We reply within one business day.