Skip to content

bug: upgrade ejs 1.0.0 -> 3.1.10 and drop ejs-locals (SNYK-JS-EJS-1049328) - #1125

Open
devin-ai-integration[bot] wants to merge 1 commit into
mainfrom
devin/sfind-0ff88c9a-ejs-3-1-10-drop-ejs-locals
Open

devin-ai-integration[bot] wants to merge 1 commit into
mainfrom
devin/sfind-0ff88c9a-ejs-3-1-10-drop-ejs-locals

Conversation

@devin-ai-integration

@devin-ai-integration devin-ai-integration Bot commented Nov 6, 2026 •

Copy link
Copy Markdown

Summary

Fixes finding sfind-0ff88c9a / SNYK-JS-EJS-1049328 (high, CWE-94): ejs@1.0.0 in package.json:24.

Bumping ejs by itself would not fix this. The EJS views are rendered through ejs-locals@1.0.2 (app.js:14), and that package brings its own nested ejs@0.8.8 (node_modules/ejs-locals/node_modules/ejs), so the top-level version was never the one doing the rendering. This PR:

  • package.json / lock: ejs 1.0.0 → 3.1.10, and removes ejs-locals (and so the nested 0.8.8). The lockfile was regenerated by npm against the registry.
  • ejs-engine.js (new): a small Express engine that supports the one ejs-locals feature the views use, <% layout('layout') %>. It renders the view, then renders layout.ejs with body set to the view's output. app.js now uses require('./ejs-engine').
    ejs.renderFile(file, locals, { cache: Boolean(options.cache) }, cb) // 4-arg form
    Compile options are passed separately from the render data. With the 3-arg form, ejs 3 still reads client and other options from the data, and it copies settings['view options'] into the compile options as-is.
  • tests/ejs-engine.test.js (node --test tests/ejs-engine.test.js, 3/3 pass). It covers: layout wrapping with the real views/index.ejs, <%= %> escaping with views/edit.ejs, and that render data with client/escape/escapeFunction/outputFunctionName/settings['view options'] cannot run code.

Duplicates #1043 (devin/1793684665-upgrade-ejs-3-1-10, same ejs 3.1.10 + ejs-locals removal), which was opened from an earlier finding. This PR also passes compile options separately and adds tests. Only one should be merged. Other open ejs PRs (#44, #282, #844, #916) only bump the top-level ejs, so ejs-locals keeps rendering with 0.8.8.

Verification

  • Engine probe: render views/index.ejs through the app's configured engine, with attacker-shaped render data:

    render data main (ejs-locals → ejs 0.8.8) this branch (ejs 3.1.10)
    {client: true, escape: "<js>"} code executed no
    escapeFunction / outputFunctionName no no
    settings: {'view options': {client, escape}} no no (it ran with the 3-arg renderFile form, which is why the 4-arg form is used)
  • Real app.js (with an in-memory Mongo stand-in, because the VM's mongod 7 doesn't work with mongoose 4.2): GET /, /login and /edit/:id return 200 on both branches. Each page has the same <title>, a single layout wrapper, no raw layout( text, markdown rendered on /, and the todo HTML-escaped in the /edit input value.

Not run: npm test (Snyk CLI, needs auth) and the full stack with real MongoDB/MySQL.

Devin-Org: engineering

Link to Devin session: https://devin-gtm.devinenterprise.com/sessions/032754aeb3744161bd96ccbed63ce6bf
Open in Devin Desktop: https://devin-gtm.devinenterprise.com/desktop/session/032754aeb3744161bd96ccbed63ce6bf?variant=devin


Devin Review

Status Commit
⚪ Not started —

Run Devin Review

Enable auto-review to review your pull requests automatically.

Devin Review (Staging)


Devin Review

…9328)

ejs-locals 1.0.2 rendered every view with its own nested ejs 0.8.8, so
bumping the top-level ejs alone would leave the vulnerable engine in use.
Replace it with a small layout('name') engine on ejs 3.1.10 that passes
compile options separately so render data can never set them.

Refs: sfind-0ff88c9aee6242a8adb9ef4b29266cd3
@devin-ai-integration

Copy link
Copy Markdown
Author

I'll fix CI failures and address comments from users with write access. I'll skip comments containing "(aside)".

  • Disable automatic comment, CI, and merge conflict monitoring

@socket-security

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Updatedejs@​0.8.8 ⏵ 3.1.10100100 +75100 +190 +40100

View full report

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔍 Devin Review: 1 flag

Not posted on this PR by your GitHub settings — view it in Devin Review. (Configure)

Devin Review

@devin-ai-integration

Copy link
Copy Markdown
Author

Browser check of 9872b53 against main. Both ran the real app, routes and templates on an in-memory Mongoose stand-in.

  • Pass: the layout renders with the title, screen.css, the Import/About footer, and no raw layout( text.
  • Pass: create a markdown todo, then edit, update, delete and reload. **bold** renders as bold and <b>x</b> stays literal (escaped).
  • Pass: login → /[REDACTED SECRET] shows Admin Access Granted.
  • No visual or functional differences from main.
Before (main) After (this PR)
main list PR list
main edit PR edit
main [REDACTED SECRET] PR [REDACTED SECRET]

Not tested: real MongoDB/MySQL persistence, the actual file-import submission, and non-EJS pages.

Written by Devin

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants