[monorepo] remove PII and variables from error messages (#31508)
* [monorepo] remove PII and variables from error messages Exclusions: - scripts - tests - fuzzing - SplitTestManager (messages are sent to admin frontend) - Group setup (we may want an error per unique tuple) - sharejs (unused types; text type errors are shadowed already) - history-v1 error messages that are used by the ErrorRecorder - errors that flag issues with configuration/call signatures I've used these search terms for finding unwanted error messages: - new Error(` - new Error\(\n\s+` (regex search) - new OError(` - new OError\(\n\s+` (regex search) * [web] throw NotFoundError from ProjectLocator * [github-sync] fix OError.tag call in script Co-authored-by: Jessica Lawshe <jessica.lawshe@overleaf.com> * [templates] revert changes to test client --------- Co-authored-by: Jessica Lawshe <jessica.lawshe@overleaf.com> GitOrigin-RevId: 736857a4fc5d9bfb0f8cb03e0f004eda87e5a220
This commit is contained in:
committed by
Copybot
co-authored by
Jessica Lawshe
parent
1a1fa497fd
commit
7c70b749d4
@@ -609,17 +609,14 @@ async function _postToClsi(
|
||||
} else if (err.response.status === 503) {
|
||||
return { response: { compile: { status: 'unavailable' } } }
|
||||
} else {
|
||||
throw new OError(
|
||||
`CLSI returned non-success code: ${err.response.status}`,
|
||||
{
|
||||
projectId,
|
||||
userId,
|
||||
compileOptions: req.compile.options,
|
||||
rootResourcePath: req.compile.rootResourcePath,
|
||||
clsiResponse: err.body,
|
||||
statusCode: err.response.status,
|
||||
}
|
||||
)
|
||||
throw new OError('CLSI returned non-success code', {
|
||||
projectId,
|
||||
userId,
|
||||
compileOptions: req.compile.options,
|
||||
rootResourcePath: req.compile.rootResourcePath,
|
||||
clsiResponse: err.body,
|
||||
statusCode: err.response.status,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
throw new OError(
|
||||
|
||||
@@ -53,13 +53,11 @@ async function deleteDoc(projectId, docId, name, deletedAt) {
|
||||
},
|
||||
})
|
||||
}
|
||||
throw new OError(
|
||||
`docstore api responded with non-success code: ${error.response.status}`,
|
||||
{
|
||||
projectId,
|
||||
docId,
|
||||
}
|
||||
)
|
||||
throw new OError('docstore api responded with non-success code', {
|
||||
projectId,
|
||||
docId,
|
||||
status: error.response.status,
|
||||
})
|
||||
}
|
||||
throw error
|
||||
}
|
||||
@@ -75,10 +73,10 @@ async function getAllDocs(projectId) {
|
||||
return await fetchJson(url, { signal: AbortSignal.timeout(TIMEOUT) })
|
||||
} catch (error) {
|
||||
if (error instanceof RequestFailedError) {
|
||||
throw new OError(
|
||||
`docstore api responded with non-success code: ${error.response.status}`,
|
||||
{ projectId }
|
||||
)
|
||||
throw new OError('docstore api responded with non-success code', {
|
||||
projectId,
|
||||
status: error.response.status,
|
||||
})
|
||||
}
|
||||
throw error
|
||||
}
|
||||
@@ -96,10 +94,10 @@ async function getAllDeletedDocs(projectId) {
|
||||
return await fetchJson(url, { signal: AbortSignal.timeout(TIMEOUT) })
|
||||
} catch (error) {
|
||||
if (error instanceof RequestFailedError) {
|
||||
throw new OError(
|
||||
`docstore api responded with non-success code: ${error.response.status}`,
|
||||
{ projectId }
|
||||
)
|
||||
throw new OError('docstore api responded with non-success code', {
|
||||
projectId,
|
||||
status: error.response.status,
|
||||
})
|
||||
}
|
||||
throw OError.tag(error, 'could not get deleted docs from docstore')
|
||||
}
|
||||
@@ -131,10 +129,10 @@ async function getAllRanges(projectId) {
|
||||
return await fetchJson(url, { signal: AbortSignal.timeout(TIMEOUT) })
|
||||
} catch (error) {
|
||||
if (error instanceof RequestFailedError) {
|
||||
throw new OError(
|
||||
`docstore api responded with non-success code: ${error.response.status}`,
|
||||
{ projectId }
|
||||
)
|
||||
throw new OError('docstore api responded with non-success code', {
|
||||
projectId,
|
||||
status: error.response.status,
|
||||
})
|
||||
}
|
||||
throw error
|
||||
}
|
||||
@@ -191,13 +189,11 @@ async function getDoc(projectId, docId, options = {}) {
|
||||
},
|
||||
})
|
||||
}
|
||||
throw new OError(
|
||||
`docstore api responded with non-success code: ${error.response.status}`,
|
||||
{
|
||||
projectId,
|
||||
docId,
|
||||
}
|
||||
)
|
||||
throw new OError('docstore api responded with non-success code', {
|
||||
projectId,
|
||||
docId,
|
||||
status: error.response.status,
|
||||
})
|
||||
}
|
||||
throw error
|
||||
}
|
||||
@@ -223,10 +219,11 @@ async function isDocDeleted(projectId, docId) {
|
||||
info: { projectId, docId },
|
||||
})
|
||||
}
|
||||
throw new OError(
|
||||
`docstore api responded with non-success code: ${error.response.status}`,
|
||||
{ projectId, docId }
|
||||
)
|
||||
throw new OError('docstore api responded with non-success code', {
|
||||
projectId,
|
||||
docId,
|
||||
status: error.response.status,
|
||||
})
|
||||
}
|
||||
throw error
|
||||
}
|
||||
@@ -258,10 +255,11 @@ async function updateDoc(projectId, docId, lines, version, ranges) {
|
||||
return { modified: result.modified, rev: result.rev }
|
||||
} catch (error) {
|
||||
if (error instanceof RequestFailedError) {
|
||||
throw new OError(
|
||||
`docstore api responded with non-success code: ${error.response.status}`,
|
||||
{ projectId, docId }
|
||||
)
|
||||
throw new OError('docstore api responded with non-success code', {
|
||||
projectId,
|
||||
docId,
|
||||
status: error.response.status,
|
||||
})
|
||||
}
|
||||
throw error
|
||||
}
|
||||
@@ -280,10 +278,10 @@ async function projectHasRanges(projectId) {
|
||||
return body.projectHasRanges
|
||||
} catch (error) {
|
||||
if (error instanceof RequestFailedError) {
|
||||
throw new OError(
|
||||
`docstore api responded with non-success code: ${error.response.status}`,
|
||||
{ projectId }
|
||||
)
|
||||
throw new OError('docstore api responded with non-success code', {
|
||||
projectId,
|
||||
status: error.response.status,
|
||||
})
|
||||
}
|
||||
throw error
|
||||
}
|
||||
@@ -332,9 +330,10 @@ async function _operateOnProject(projectId, method) {
|
||||
})
|
||||
} catch (err) {
|
||||
if (err instanceof RequestFailedError) {
|
||||
const error = new Error(
|
||||
`docstore api responded with non-success code: ${err.response.status}`
|
||||
)
|
||||
const error = new OError('docstore api responded with non-success code', {
|
||||
projectId,
|
||||
status: err.response.status,
|
||||
})
|
||||
logger.warn(
|
||||
{ err: error, projectId },
|
||||
`error calling ${method} project in docstore`
|
||||
|
||||
@@ -213,8 +213,8 @@ export default ExportsHandler = {
|
||||
return callback(null, body.version)
|
||||
} else {
|
||||
err = new OError(
|
||||
`project history version returned a failure status code: ${res.statusCode}`,
|
||||
{ project_id: projectId }
|
||||
'project history version returned a failure status code',
|
||||
{ project_id: projectId, statusCode: res.statusCode }
|
||||
)
|
||||
return callback(err)
|
||||
}
|
||||
@@ -241,10 +241,10 @@ export default ExportsHandler = {
|
||||
} else if (res.statusCode >= 200 && res.statusCode < 300) {
|
||||
return callback(null, body)
|
||||
} else {
|
||||
err = new OError(
|
||||
`v1 export returned a failure status code: ${res.statusCode}`,
|
||||
{ export: exportId }
|
||||
)
|
||||
err = new OError('v1 export returned a failure status code', {
|
||||
export: exportId,
|
||||
statusCode: res.statusCode,
|
||||
})
|
||||
return callback(err)
|
||||
}
|
||||
}
|
||||
@@ -270,10 +270,10 @@ export default ExportsHandler = {
|
||||
} else if (res.statusCode >= 200 && res.statusCode < 300) {
|
||||
return callback(null, body)
|
||||
} else {
|
||||
err = new OError(
|
||||
`v1 export returned a failure status code: ${res.statusCode}`,
|
||||
{ export: exportId }
|
||||
)
|
||||
err = new OError('v1 export returned a failure status code', {
|
||||
export: exportId,
|
||||
statusCode: res.statusCode,
|
||||
})
|
||||
return callback(err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ import LinkedFilesHandler from './LinkedFilesHandler.mjs'
|
||||
import LinkedFilesErrors from './LinkedFilesErrors.mjs'
|
||||
import { promisify } from '@overleaf/promise-utils'
|
||||
import HistoryManager from '../History/HistoryManager.mjs'
|
||||
import Errors from '../Errors/Errors.js'
|
||||
|
||||
const {
|
||||
BadDataError,
|
||||
@@ -197,7 +198,7 @@ export default ProjectFileAgent = {
|
||||
},
|
||||
function (err, entity, type) {
|
||||
if (err != null) {
|
||||
if (/^not found.*/.test(err.message)) {
|
||||
if (err instanceof Errors.NotFoundError) {
|
||||
err = new SourceFileNotFoundError()
|
||||
}
|
||||
return callback(err)
|
||||
|
||||
@@ -2,6 +2,7 @@ import mongodb from 'mongodb-legacy'
|
||||
|
||||
import _ from 'lodash'
|
||||
import Settings from '@overleaf/settings'
|
||||
import OError from '@overleaf/o-error'
|
||||
|
||||
const { ObjectId } = mongodb
|
||||
|
||||
@@ -90,7 +91,7 @@ function ensureNameIsUnique(nameList, name, suffixes, maxLength) {
|
||||
if (uniqueName != null) {
|
||||
return uniqueName
|
||||
} else {
|
||||
throw new Error(`Failed to generate a unique name for: ${name}`)
|
||||
throw new OError('Failed to generate a unique name', { name })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -167,9 +167,11 @@ async function _findElementByPathWithProject(
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
throw new Error(
|
||||
`not found project: ${project._id} search path: ${needlePath}, folder ${foldersList[level]} could not be found`
|
||||
)
|
||||
throw new Errors.NotFoundError('parent folder not found in project', {
|
||||
projectId: project._id,
|
||||
needlePath,
|
||||
needleFolderName,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -202,9 +204,11 @@ async function _findElementByPathWithProject(
|
||||
if (result != null) {
|
||||
return { element: result, type, folder }
|
||||
}
|
||||
throw new Error(
|
||||
`not found project: ${project._id} search path: ${needlePath}, entity ${entityName} could not be found`
|
||||
)
|
||||
throw new Errors.NotFoundError('element not found in project', {
|
||||
projectId: project._id,
|
||||
needlePath,
|
||||
entityName,
|
||||
})
|
||||
}
|
||||
|
||||
if (project == null) {
|
||||
|
||||
@@ -4,6 +4,7 @@ import { callbackify } from 'node:util'
|
||||
import { db, ObjectId } from '../../infrastructure/mongodb.mjs'
|
||||
import Errors from '../Errors/Errors.js'
|
||||
import mongodb from 'mongodb-legacy'
|
||||
import OError from '@overleaf/o-error'
|
||||
const safeCompilers = ['xelatex', 'pdflatex', 'latex', 'lualatex']
|
||||
|
||||
const { ReturnDocument } = mongodb
|
||||
@@ -16,7 +17,7 @@ const ProjectOptionsHandler = {
|
||||
normalizeCompiler(compiler) {
|
||||
compiler = compiler.toLowerCase()
|
||||
if (!safeCompilers.includes(compiler)) {
|
||||
throw new Error(`invalid compiler: ${compiler}`)
|
||||
throw new OError('invalid compiler', { compiler })
|
||||
}
|
||||
return compiler
|
||||
},
|
||||
@@ -44,7 +45,7 @@ const ProjectOptionsHandler = {
|
||||
allowed => imageName === allowed.imageName
|
||||
)
|
||||
if (!isAllowed) {
|
||||
throw new Error(`invalid imageName: ${imageName}`)
|
||||
throw new OError('invalid imageName', { imageName })
|
||||
}
|
||||
return settings.imageRoot + '/' + imageName
|
||||
},
|
||||
@@ -67,7 +68,7 @@ const ProjectOptionsHandler = {
|
||||
language => language.code === languageCode
|
||||
)
|
||||
if (languageCode && !language) {
|
||||
throw new Error(`invalid languageCode: ${languageCode}`)
|
||||
throw new OError('invalid languageCode', { languageCode })
|
||||
}
|
||||
const conditions = { _id: projectId }
|
||||
const update = { spellCheckLanguage: languageCode }
|
||||
|
||||
@@ -2,6 +2,7 @@ import _ from 'lodash'
|
||||
import { callbackify } from 'node:util'
|
||||
import { User } from '../../models/User.mjs'
|
||||
import Settings from '@overleaf/settings'
|
||||
import OError from '@overleaf/o-error'
|
||||
|
||||
const ReferalFeatures = {
|
||||
async getBonusFeatures(userId) {
|
||||
@@ -9,7 +10,7 @@ const ReferalFeatures = {
|
||||
const user = await User.findOne(query, { refered_user_count: 1 }).exec()
|
||||
|
||||
if (user == null) {
|
||||
throw new Error(`user not found ${userId} for assignBonus`)
|
||||
throw new OError('user not found for assignBonus', { userId })
|
||||
}
|
||||
|
||||
if (user.refered_user_count != null && user.refered_user_count > 0) {
|
||||
|
||||
@@ -446,10 +446,9 @@ const promises = {
|
||||
},
|
||||
'error returned from recurly'
|
||||
)
|
||||
throw new OError(
|
||||
`Recurly API returned with status code: ${error.response.status}`,
|
||||
{ statusCode: error.response.status }
|
||||
)
|
||||
throw new OError('Recurly API returned with status code', {
|
||||
statusCode: error.response.status,
|
||||
})
|
||||
} else {
|
||||
throw error
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ import request from 'requestretry'
|
||||
import settings from '@overleaf/settings'
|
||||
import { V1ConnectionError, NotFoundError } from '../Errors/Errors.js'
|
||||
import { promisify } from '@overleaf/promise-utils'
|
||||
import OError from '@overleaf/o-error'
|
||||
|
||||
const V1SubscriptionManager = {
|
||||
cancelV1Subscription(userId, callback) {
|
||||
@@ -104,11 +105,11 @@ const V1SubscriptionManager = {
|
||||
return callback(new NotFoundError(`v1 user not found: ${userId}`))
|
||||
} else {
|
||||
return callback(
|
||||
new Error(
|
||||
`non-success code from v1: ${response.statusCode} ${
|
||||
options.method
|
||||
} ${options.url(v1Id)}`
|
||||
)
|
||||
new OError('non-success code from v1', {
|
||||
url: options.url(v1Id),
|
||||
method: options.method,
|
||||
statusCode: response.statusCode,
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ import Errors from '../Errors/Errors.js'
|
||||
import { pipeline } from 'node:stream/promises'
|
||||
import ClsiCacheManager from '../Compile/ClsiCacheManager.mjs'
|
||||
import Path from 'node:path'
|
||||
import OError from '@overleaf/o-error'
|
||||
|
||||
const { promises: ProjectRootDocManager } = ProjectRootDocManagerModule
|
||||
const { promises: ProjectOptionsHandler } = ProjectOptionsHandlerModule
|
||||
@@ -64,7 +65,7 @@ const TemplatesManager = {
|
||||
{ uri: zipUrl, statusCode: zipReq.response.status },
|
||||
'non-success code getting zip from template API'
|
||||
)
|
||||
throw new Error(`get zip failed: ${zipReq.response.status}`)
|
||||
throw new OError('get zip failed', { status: zipReq.response.status })
|
||||
}
|
||||
const { fileEntries, docEntries, project } =
|
||||
await ProjectUploadManager.promises.createProjectFromZipArchiveWithName(
|
||||
|
||||
@@ -2,6 +2,7 @@ import crypto from 'node:crypto'
|
||||
import V1Api from '../V1/V1Api.mjs'
|
||||
import Features from '../../infrastructure/Features.mjs'
|
||||
import { callbackify } from 'node:util'
|
||||
import OError from '@overleaf/o-error'
|
||||
|
||||
// (From Overleaf `random_token.rb`)
|
||||
// Letters (not numbers! see generate_token) used in tokens. They're all
|
||||
@@ -59,13 +60,13 @@ async function generateUniqueReadOnlyToken() {
|
||||
})
|
||||
|
||||
if (response.statusCode !== 200) {
|
||||
throw new Error(
|
||||
`non-200 response from v1 read-token-exists api: ${response.statusCode}`
|
||||
)
|
||||
throw new OError('non-200 response from v1 read-token-exists api', {
|
||||
statusCode: response.statusCode,
|
||||
})
|
||||
}
|
||||
|
||||
if (body.exists === true) {
|
||||
throw new Error(`token already exists in v1: ${token}`)
|
||||
throw new OError('token already exists in v1', { token })
|
||||
}
|
||||
|
||||
return token
|
||||
|
||||
@@ -6,6 +6,7 @@ import Errors from '../Errors/Errors.js'
|
||||
import FileTypeManager from './FileTypeManager.mjs'
|
||||
import SafePath from '../Project/SafePath.mjs'
|
||||
import logger from '@overleaf/logger'
|
||||
import OError from '@overleaf/o-error'
|
||||
|
||||
export default {
|
||||
addEntity: callbackify(addEntity),
|
||||
@@ -183,7 +184,7 @@ async function _isSafeOnFileSystem(path) {
|
||||
async function importFile(fsPath, projectPath) {
|
||||
const stat = await fs.promises.lstat(fsPath)
|
||||
if (!stat.isFile()) {
|
||||
throw new Error(`can't import ${fsPath}: not a regular file`)
|
||||
throw new OError("can't import file: not a regular file", { fsPath })
|
||||
}
|
||||
_validateProjectPath(projectPath)
|
||||
const filename = Path.basename(projectPath)
|
||||
@@ -206,7 +207,7 @@ async function importFile(fsPath, projectPath) {
|
||||
async function importDir(dirPath) {
|
||||
const stat = await fs.promises.lstat(dirPath)
|
||||
if (!stat.isDirectory()) {
|
||||
throw new Error(`can't import ${dirPath}: not a directory`)
|
||||
throw new OError("can't import dir: not a directory", { dirPath })
|
||||
}
|
||||
const entries = []
|
||||
for await (const filePath of _walkDir(dirPath)) {
|
||||
|
||||
@@ -207,9 +207,11 @@ function _sendUnlinkedEmail(primaryEmail, providerName, institutionEmail) {
|
||||
|
||||
async function getUser(providerId, externalUserId, userIdAttribute) {
|
||||
if (!providerId || !externalUserId || !userIdAttribute) {
|
||||
throw new Error(
|
||||
`invalid arguments: providerId: ${providerId}, externalUserId: ${externalUserId}, userIdAttribute: ${userIdAttribute}`
|
||||
)
|
||||
throw new OError('invalid arguments', {
|
||||
providerId,
|
||||
externalUserId,
|
||||
userIdAttribute,
|
||||
})
|
||||
}
|
||||
const user = await User.findOne({
|
||||
samlIdentifiers: {
|
||||
@@ -252,9 +254,7 @@ async function linkAccounts(userId, samlData, auditLog) {
|
||||
} = samlData
|
||||
|
||||
if (!externalUserId || !institutionEmail || !providerId || !userIdAttribute) {
|
||||
throw new Error(
|
||||
`missing data when linking institution SSO: ${JSON.stringify(samlData)}`
|
||||
)
|
||||
throw new OError('missing data when linking institution SSO', { samlData })
|
||||
}
|
||||
|
||||
await _addIdentifier(
|
||||
|
||||
@@ -12,6 +12,7 @@ import request from 'request'
|
||||
import settings from '@overleaf/settings'
|
||||
import Errors from '../Errors/Errors.js'
|
||||
import { promisifyMultiResult } from '@overleaf/promise-utils'
|
||||
import OError from '@overleaf/o-error'
|
||||
|
||||
// TODO: check what happens when these settings aren't defined
|
||||
const DEFAULT_V1_PARAMS = {
|
||||
@@ -89,9 +90,11 @@ const V1Api = {
|
||||
error.statusCode = response.statusCode
|
||||
return callback(error)
|
||||
} else {
|
||||
error = new Error(
|
||||
`overleaf v1 returned non-success code: ${response?.statusCode} ${options.method} ${options.uri}`
|
||||
)
|
||||
error = new OError('overleaf v1 returned non-success code', {
|
||||
status: response?.statusCode,
|
||||
method: options.method,
|
||||
url: options.uri,
|
||||
})
|
||||
error.statusCode = response?.statusCode
|
||||
return callback(error)
|
||||
}
|
||||
|
||||
@@ -45,9 +45,9 @@ export default V1Handler = {
|
||||
)
|
||||
return callback(null, isValid, userProfile)
|
||||
} else {
|
||||
err = new Error(
|
||||
`Unexpected status from v1 login api: ${response.statusCode}`
|
||||
)
|
||||
err = new OError('Unexpected status from v1 login api', {
|
||||
status: response.statusCode,
|
||||
})
|
||||
return callback(err)
|
||||
}
|
||||
}
|
||||
@@ -79,9 +79,9 @@ export default V1Handler = {
|
||||
)
|
||||
return callback(null, true)
|
||||
} else {
|
||||
err = new Error(
|
||||
`Unexpected status from v1 password reset api: ${response.statusCode}`
|
||||
)
|
||||
err = new OError('Unexpected status from v1 password reset api', {
|
||||
status: response.statusCode,
|
||||
})
|
||||
return callback(err, false)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -172,7 +172,9 @@ const FileWriter = {
|
||||
if (response.statusCode >= 200 && response.statusCode < 300) {
|
||||
FileWriter.writeStreamToDisk(identifier, stream, options, callback)
|
||||
} else {
|
||||
const err = new Error(`bad response from url: ${response.statusCode}`)
|
||||
const err = new OError('bad response from url', {
|
||||
statusCode: response.statusCode,
|
||||
})
|
||||
logger.warn({ err, identifier, url }, `[writeUrlToDisk] ${err.message}`)
|
||||
return callback(err)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user