mirror of
https://github.com/yu-i-i/overleaf-cep.git
synced 2026-11-14 14:13:43 +01:00
[monorepo] prepare for zod everywhere epic (#36027)
GitOrigin-RevId: f082ae7fc7857fa72b2c1823645050bd4f6df305
This commit is contained in:
@@ -7,6 +7,9 @@ const {
|
||||
GCEMetadataLogLevelChecker,
|
||||
} = require('./log-level-checker')
|
||||
const { setLogger } = require('@overleaf/fetch-utils')
|
||||
const {
|
||||
setLogger: setValidationToolsLogger,
|
||||
} = require('@overleaf/validation-tools')
|
||||
|
||||
const LoggingManager = {
|
||||
/**
|
||||
@@ -33,6 +36,7 @@ const LoggingManager = {
|
||||
this._setupRingBuffer()
|
||||
this._setupLogLevelChecker()
|
||||
setLogger(this)
|
||||
setValidationToolsLogger(this)
|
||||
return this
|
||||
},
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
"@google-cloud/logging-bunyan": "^5.1.0",
|
||||
"@overleaf/fetch-utils": "workspace:*",
|
||||
"@overleaf/o-error": "workspace:*",
|
||||
"@overleaf/validation-tools": "workspace:*",
|
||||
"bunyan": "^1.8.14"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
const OError = require('@overleaf/o-error')
|
||||
const { getRawReqInput } = require('@overleaf/validation-tools')
|
||||
|
||||
function errSerializer(err) {
|
||||
if (!err) {
|
||||
@@ -36,11 +37,14 @@ function reqSerializer(req) {
|
||||
'content-length': headers['content-length'],
|
||||
},
|
||||
}
|
||||
if (req.params) {
|
||||
const projectId =
|
||||
req.params.projectId || req.params.project_id || req.params.Project_id
|
||||
const userId = req.params.userId || req.params.user_id
|
||||
const docId = req.params.docId || req.params.doc_id
|
||||
// raw access justified: case 2 (final error-handler logging) — this
|
||||
// serializer must not throw while extracting log context, and bunyan
|
||||
// silently drops the whole `req` field (with a stderr warning) if it did
|
||||
const { params } = getRawReqInput(req)
|
||||
if (params) {
|
||||
const projectId = params.projectId || params.project_id || params.Project_id
|
||||
const userId = params.userId || params.user_id
|
||||
const docId = params.docId || params.doc_id
|
||||
if (projectId) {
|
||||
entry.projectId = projectId
|
||||
}
|
||||
|
||||
@@ -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')
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user