Authorization analysis logic improvements (#61)

This commit is contained in:
Eli Peter 2026-05-02 16:44:49 -04:00 committed by GitHub
parent 3c89bddbf2
commit 40995e45e7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
55 changed files with 4193 additions and 134 deletions

View file

@ -0,0 +1,60 @@
// Nyx CVE benchmark fixture.
//
// CVE: CVE-2023-22621
// Project: Strapi (strapi/strapi)
// License: MIT (https://github.com/strapi/strapi/blob/develop/LICENSE)
// Advisory: https://github.com/strapi/strapi/security/advisories/GHSA-2h87-4q2w-v4hf
// Patched: 921d30961d6ba96cc098f2aea197350a49f990bd
// packages/core/email/server/services/email.js:25-50
//
// Patched-fix simplification: `createStrictInterpolationRegExp` is
// imported from `@strapi/utils` upstream; we inline a one-line stub
// that builds a regex restricted to a fixed allowlist. The load-
// bearing fix is the explicit `{ interpolate, evaluate: false,
// escape: false }` options object passed to `_.template`, which
// disables lodash's `<% ... %>` evaluate block. The trailing
// `(data)` invocation of the compiled function is split off (matches
// the corresponding split in `vulnerable.js`).
//
// Trim parity with `vulnerable.js`: same `attributes.reduce`-to-`for`
// transformation; the load-bearing
// `_.template(emailTemplate[attribute], { interpolate, evaluate: false, escape: false })`
// call is verbatim from upstream's options-object form.
'use strict';
const _ = require('lodash');
const express = require('express');
const app = express();
app.use(express.json());
const createStrictInterpolationRegExp = (allowed) =>
new RegExp(`<%=\\s*(${allowed.join('|')})\\s*%>`, 'g');
const keysDeep = (obj) => Object.keys(obj || {});
const sendTemplatedEmail = (emailOptions = {}, emailTemplate = {}, data = {}) => {
const attributes = ['subject', 'text', 'html'];
const allowedInterpolationVariables = keysDeep(data);
const interpolate = createStrictInterpolationRegExp(allowedInterpolationVariables);
const templatedAttributes = {};
for (const attribute of attributes) {
if (emailTemplate[attribute]) {
const compiled = _.template(emailTemplate[attribute], {
interpolate,
evaluate: false,
escape: false,
});
templatedAttributes[attribute] = compiled(data);
}
}
return templatedAttributes;
};
app.put('/users-permissions/email-templates', (req, res) => {
sendTemplatedEmail({}, req.body.emailTemplate, req.body.data);
res.sendStatus(200);
});
app.listen(1337);

View file

@ -0,0 +1,50 @@
// Nyx CVE benchmark fixture.
//
// CVE: CVE-2023-22621
// Project: Strapi (strapi/strapi)
// License: MIT (https://github.com/strapi/strapi/blob/develop/LICENSE)
// Advisory: https://github.com/strapi/strapi/security/advisories/GHSA-2h87-4q2w-v4hf
// Vulnerable: 479bdde67eb3759d89218c9686208be2409217ef
// packages/core/email/server/services/email.js:23-39
//
// Strapi <= 4.5.5 compiled email-template strings via lodash `_.template`
// without restricting the interpolation regex. An authenticated admin
// could PUT /users-permissions/email-templates with a payload whose
// `subject` / `text` / `html` field contained a lodash `<% ... %>`
// evaluate block, which lodash compiles into a JavaScript Function. When
// the email service rendered the template, the embedded JavaScript
// executed in the Strapi process context (RCE).
//
// Trims: `keysDeep` import, `missingAttributes` early-throw, plugin
// provider chain, the surrounding controller layer that translates
// `PUT /email-templates` into a call to `sendTemplatedEmail`. The
// load-bearing sink call `_.template(emailTemplate[attribute])` is
// verbatim; the trailing `(data)` invocation of the compiled
// function is split off so the engine sees the SSTI sink directly
// rather than as the inner call of a `f()()` chain.
'use strict';
const _ = require('lodash');
const express = require('express');
const app = express();
app.use(express.json());
const sendTemplatedEmail = (emailOptions = {}, emailTemplate = {}, data = {}) => {
const attributes = ['subject', 'text', 'html'];
const templatedAttributes = {};
for (const attribute of attributes) {
if (emailTemplate[attribute]) {
const compiled = _.template(emailTemplate[attribute]);
templatedAttributes[attribute] = compiled(data);
}
}
return templatedAttributes;
};
app.put('/users-permissions/email-templates', (req, res) => {
sendTemplatedEmail({}, req.body.emailTemplate, req.body.data);
res.sendStatus(200);
});
app.listen(1337);