[monorepo] prepare for zod everywhere epic (#36027)

GitOrigin-RevId: f082ae7fc7857fa72b2c1823645050bd4f6df305
This commit is contained in:
Jakob Ackermann
2026-09-17 11:42:46 +00:00
committed by Copybot
parent bff27fc3cc
commit 81df324bcc
27 changed files with 2588 additions and 70 deletions
+5
View File
@@ -7,6 +7,11 @@ chai.should()
chai.use(sinonChai)
SandboxedModule.configure({
requires: {
// pulls in mongodb (via zod ObjectId helpers), which does not compile in
// the sandbox (missing TextEncoder)
'@overleaf/validation-tools': require('@overleaf/validation-tools'),
},
globals: { Buffer, JSON, console, process },
sourceTransformers: {
removeNodePrefix: function (source) {
@@ -46,10 +46,14 @@ describe('LoggingManager', function () {
this.fetchUtils = {
setLogger: sinon.stub(),
}
this.validationTools = {
setLogger: sinon.stub(),
}
this.LoggingManager = SandboxedModule.require(MODULE_PATH, {
requires: {
bunyan: this.Bunyan,
'@overleaf/fetch-utils': this.fetchUtils,
'@overleaf/validation-tools': this.validationTools,
'./log-level-checker': this.LogLevelChecker,
},
})
@@ -0,0 +1,137 @@
const path = require('node:path')
const { expect } = require('chai')
const MODULE_PATH = path.join(__dirname, '../../serializers.js')
const serializers = require(MODULE_PATH)
// Mirrors the symbols the patched express (.yarn/patches/express-npm-*.patch)
// installs on req in REQ_LOCKDOWN_MODE=warn|throw, and how
// @overleaf/validation-tools' getRawReqInput() reads them.
const RAW_BODY = Symbol.for('overleaf.lockdown.rawBody')
const RAW_QUERY = Symbol.for('overleaf.lockdown.rawQuery')
const RAW_PARAMS = Symbol.for('overleaf.lockdown.rawParams')
const INSTALLED = Symbol.for('overleaf.lockdown.installed')
// Replicates what the patched express does to a request in throw mode:
// parsed input lives in symbol-keyed fields, the public `params` getter
// throws instead of returning it.
function lockedRequest({ params, ...rest }) {
const req = { ...rest }
req[INSTALLED] = true
req[RAW_PARAMS] = params
req[RAW_QUERY] = {}
req[RAW_BODY] = undefined
Object.defineProperty(req, 'params', {
configurable: true,
enumerable: false,
get() {
throw new Error('raw request input is forbidden (req.params)')
},
})
return req
}
describe('reqSerializer', function () {
it('returns undefined/falsy req untouched', function () {
expect(serializers.req(null)).to.equal(null)
expect(serializers.req(undefined)).to.equal(undefined)
})
it('extracts method, url, headers and remote address', function () {
const req = {
method: 'GET',
originalUrl: '/project/123',
headers: {
referer: 'https://example.com',
'user-agent': 'test-agent',
'content-length': '42',
},
ip: '127.0.0.1',
params: {},
}
expect(serializers.req(req)).to.deep.equal({
method: 'GET',
url: '/project/123',
remoteAddress: '127.0.0.1',
headers: {
referer: 'https://example.com',
'user-agent': 'test-agent',
'content-length': '42',
},
})
})
it('extracts projectId/userId/docId from ordinary, readable req.params', function () {
const req = {
method: 'GET',
url: '/',
headers: {},
params: {
project_id: 'project-1',
user_id: 'user-1',
doc_id: 'doc-1',
},
}
const entry = serializers.req(req)
expect(entry.projectId).to.equal('project-1')
expect(entry.userId).to.equal('user-1')
expect(entry.docId).to.equal('doc-1')
})
it('prefers the camelCase param name when both spellings are present', function () {
const req = {
method: 'GET',
url: '/',
headers: {},
params: {
projectId: 'camel-project',
project_id: 'snake-project',
userId: 'camel-user',
user_id: 'snake-user',
docId: 'camel-doc',
doc_id: 'snake-doc',
},
}
const entry = serializers.req(req)
expect(entry.projectId).to.equal('camel-project')
expect(entry.userId).to.equal('camel-user')
expect(entry.docId).to.equal('camel-doc')
})
it('omits projectId/userId/docId when req.params has none of the recognised fields', function () {
const req = { method: 'GET', url: '/', headers: {}, params: {} }
const entry = serializers.req(req)
expect(entry).to.not.have.property('projectId')
expect(entry).to.not.have.property('userId')
expect(entry).to.not.have.property('docId')
})
it('does not throw when req.params is a lockdown-installed throwing getter, and still extracts the ids via the raw accessor', function () {
const req = lockedRequest({
method: 'GET',
url: '/project/123',
headers: {},
params: {
project_id: 'project-1',
user_id: 'user-1',
doc_id: 'doc-1',
},
})
// Sanity check: touching req.params directly really does throw, the way
// it would under REQ_LOCKDOWN_MODE=throw.
expect(() => req.params).to.throw(/raw request input is forbidden/)
let entry
expect(() => {
entry = serializers.req(req)
}).to.not.throw()
expect(entry.projectId).to.equal('project-1')
expect(entry.userId).to.equal('user-1')
expect(entry.docId).to.equal('doc-1')
})
})