Page MenuHomePhabricator

Application Security Review Request : EditCheck server-side suggestions
Open, HighPublic5 Estimated Story Points

Description

Project Information

Description of the tool/project:
EditCheck suggestions are generated in the client, using logic that evaluates the VisualEditor data model. We would like to show these suggestions outside of the editor, initially as a counter in read mode. We have considered multiple approaches for doing this:

  • Running VE in Node. Cons: while we had some success with core VE, we also have lots of MediaWiki dependencies (VE code in extensions, MW libraries, API calls) and this quickly becomes running all of MW in Node.
  • Creating server-side duplicates of all the checks that run directly against Parsoid HTML. Cons: This would requires rewriting and maintaining a duplicate copy of most of the EditCheck system, as well as many parts of VE's converter and some MW dependencies.
  • Taking results from the user's client and posting them to the server. Cons: Clients could generate bad data either accidentally (broken browser, or browser with excessive extensions installed) or maliciously.

The approach we have decided on is to run EditCheck headless via a very lightweight entry point (Special:EditCheckHeadless). VE will happily run headless (we already do this for visual diffs in the user's client) as will the edit check logic. The data can then be sent back through an API via Puppeteer.

Description of how the tool will be used at WMF:
Edit suggestions will be counted after a page has been edited (triggered by a JobQueue). The results of the count will be inserted into the Linked Artifact Cache. The API is fully internal, it will never be publicly accessible.

  • editcheck-headless-server opens a headless browser at /Special:EditCheckHeadless
    • this page can be locked down as much as needed - it only needs to load RL modules, and make action API calls.
  • an internal request from the JobQueue comes in, asking for the suggestion count for <Page>
  • <Page>'s Parsoid HTML is fetched in the headless browser, loaded into VE and the edit check logic run
    • Parsoid HTML could also be fetched in advance and POSTed to the API, if required (still, only internally)
  • the result is handed back to Puppeteer
  • the browser can be shutdown after each request, or be kept open for performance and restarted every N requests (to avoid memory leaks)

We are open to using headless Chrome, or more lightweight render-free headless browsers like Lightpanda which uses ~10x fewer resources (CPU/memory).

Dependencies

List dependencies, or upstream projects that this project relies on.

  • Puppeteer (open to alternatives like WebDriver if required)
  • Chromium headless (open to alternatives like Lightpanda)
  • gRPC

Has this project been reviewed before?
No

Working test environment
e.g.

node editcheck/build/editcheck-headless/editcheck-headless-server.js  --script-path <script-path> --wikis http://localhost

Post-deployment
Editing, contacts as above

Details

Risk Rating
Low
Author Affiliation
WMF Product

Related Objects

StatusSubtypeAssignedTask
OpenNone
OpenNone
OpenNone
Openppelberg
Openppelberg
OpenNone
OpenEsanders
Opendchan
OpenGGoncalves-WMF
Openppelberg
Openppelberg
OpenNone
OpenNone
OpenMNeisler
OpenOttomata
Openbmartinezcalvo
OpenNone
DeclinedNone
Openbmartinezcalvo
ResolvedBUG REPORTmedelius
OpenNone
Opencjming
OpenNone
OpenNone
OpenEsanders
OpenRscout
OpenNone
OpenNone

Event Timeline

Peachey88 renamed this task from Application Security Review Request : ... to Application Security Review Request : EditCheck server-side suggestions.Tue, Jul 21, 10:29 AM
ppelberg set the point value for this task to 5.Wed, Aug 5, 5:18 PM

Alright, to get us started I ran some of the tooling from the how-to in the meantime:

ToolScopeResult
npm auditpackage-lock.json (--package-lock-only --omit=peer --omit=optional)0 vulnerabilities
osv-scannerpackage-lock.jsonNo issues, 145 packages
semgrep21 tracked files, 274 rules3 findings - 1 valid (Low), 2 false positives, see below
gitleaksFull git history, 19 commitsNo leaks, full 19-commit history

semgrep triage:

VulnerabilityPackageNotesServiceRemediationRisk
This .npmrc does not set a minimum release age or sets it too low. Newly published packages can be malicious or unstable. Add min-release-age = 7 to wait 7 days before resolving newly published package versions. Added in: v11.10. Reference: https://github.blog/changelog/2026-02-18-npm-bulk-trusted-publishing-config-and-script-security-now-generally-available/.npmrc:1Valid. Newly published packages can be malicious; npm resolves them immediately. Scope is narrow, the lockfile is committed and builds install from it with integrity hashes, so this hardens the dependency-update workflow, not production.semgrepAdd min-release-age = 7. Verify the npm in nodejs24-slim is ≥ 11.10; an unrecognised key is silently ignored.Low
A hardcoded username in plain text is identified. Store it properly in an environment variable.editcheck-headless-grpc.js:44-45False positive. The flagged constant is the outbound User-Agent string, which WMF API policy requires. No credential exists; the njsscan rule matched on the identifier USER_AGENT.semgrepNone, code is correct as written.None
Checks for any usage of http servers instead of https servers. Encourages the usage of https protocol instead of http, which does not have TLS and is therefore unencrypted. Using http can lead to man-in-the-middle attacks in which the attacker is able to read sensitive information.editcheck-headless-server.js:698False positive in context. Plain HTTP on a pod-local listener is the standard WMF pattern - Envoy terminates TLS. Corroborates the same undocumented assumption as the gRPC createInsecure() listener.semgrepDocument the TLS-termination assumption in the repo.Informational

And the report generated by the standalone-vuln-audit Claude skill that Riku recommended. The findings are reasonable, imo.