Merge pull request #24390 from overleaf/em-enforce-content-hash-validation

Enforce content hash validation in history

GitOrigin-RevId: 90de21ea86ddc6548001059c41139a2af5b27060
This commit is contained in:
Eric Mc Sween
2025-03-24 10:50:01 +00:00
committed by Copybot
parent 7308ac0e1f
commit 0e9c310d1d
4 changed files with 11 additions and 31 deletions
@@ -22,6 +22,7 @@ const BlobStore = storage.BlobStore
const chunkStore = storage.chunkStore
const HashCheckBlobStore = storage.HashCheckBlobStore
const persistChanges = storage.persistChanges
const InvalidChangeError = storage.InvalidChangeError
const render = require('./render')
@@ -113,7 +114,8 @@ async function importChanges(req, res, next) {
err instanceof File.NotEditableError ||
err instanceof FileMap.PathnameError ||
err instanceof Snapshot.EditMissingFileError ||
err instanceof chunkStore.ChunkVersionConflictError
err instanceof chunkStore.ChunkVersionConflictError ||
err instanceof InvalidChangeError
) {
// If we failed to apply operations, that's probably because they were
// invalid.
+3
View File
@@ -15,3 +15,6 @@ exports.zipStore = require('./lib/zip_store')
const { BlobStore, loadGlobalBlobs } = require('./lib/blob_store')
exports.BlobStore = BlobStore
exports.loadGlobalBlobs = loadGlobalBlobs
const { InvalidChangeError } = require('./lib/errors')
exports.InvalidChangeError = InvalidChangeError
@@ -81,12 +81,6 @@ async function persistChanges(projectId, allChanges, limits, clientEndVersion) {
let originalEndVersion
let changesToPersist
/**
* It's only useful to log validation errors once per flush. When we enforce
* content hash validation, it will stop the flush right away anyway.
*/
let validationErrorLogged = false
limits = limits || {}
_.defaults(limits, {
changeBucketMinutes: 60,
@@ -131,22 +125,7 @@ async function persistChanges(projectId, allChanges, limits, clientEndVersion) {
for (const operation of change.iterativelyApplyTo(currentSnapshot, {
strict: true,
})) {
try {
await validateContentHash(operation)
} catch (err) {
// Temporary: skip validation errors
if (err instanceof InvalidChangeError) {
if (!validationErrorLogged) {
logger.warn(
{ err, projectId },
'content snapshot mismatch (ignored)'
)
validationErrorLogged = true
}
} else {
throw err
}
}
await validateContentHash(operation)
}
chunk.pushChanges([change])
@@ -213,7 +213,7 @@ describe('persistChanges', function () {
expect(result.numberOfChangesPersisted).to.equal(1)
})
it('acccepts a change with an invalid hash (only logs for now)', async function () {
it('rejects a change with an invalid hash', async function () {
const limitsToPersistImmediately = {
minChangeTimestamp: farFuture,
maxChangeTimestamp: farFuture,
@@ -235,13 +235,9 @@ describe('persistChanges', function () {
)
const changes = [change]
const result = await persistChanges(
projectId,
changes,
limitsToPersistImmediately,
0
)
expect(result.numberOfChangesPersisted).to.equal(1)
await expect(
persistChanges(projectId, changes, limitsToPersistImmediately, 0)
).to.be.rejectedWith(storage.InvalidChangeError)
})
})
})