Skip to content

Commit 86a43b8

Browse files
committed
Harder checks against objects supplied in the bodies of bulk endpoints.
1 parent e88c769 commit 86a43b8

1 file changed

Lines changed: 56 additions & 44 deletions

File tree

db-controller.js

Lines changed: 56 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1000,13 +1000,20 @@ const bulkCreate = async function (req, res, next) {
10001000
next(createExpressError(err))
10011001
return
10021002
}
1003-
let gate = documents.filter(d=> {
1003+
let gatekeep = documents.filter(d=> {
1004+
// Each item must be valid JSON, but can't be an array.
10041005
if(Array.isArray(d) || typeof d !== "object") return d
1006+
try {
1007+
JSON.parse(JSON.stringify(d))
1008+
} catch (err) {
1009+
return d
1010+
}
1011+
// Items must not have an @id, and in some cases same for id.
10051012
const idcheck = _contextid(d["@context"]) ? d.id ?? d["@id"] : d["@id"]
10061013
if(idcheck) return d
10071014
})
1008-
if (gate.length > 1) {
1009-
err.message = "All objects in the body of a `/bulkCreate` must be JSON and must not contain an @id or id property."
1015+
if (gatekeep.length > 1) {
1016+
err.message = "All objects in the body of a `/bulkCreate` must be JSON and must not contain a declared identifier property."
10101017
err.status = 400
10111018
next(createExpressError(err))
10121019
return
@@ -1072,13 +1079,20 @@ const bulkUpdate = async function (req, res, next) {
10721079
next(createExpressError(err))
10731080
return
10741081
}
1075-
let gate = documents.filter(d => {
1082+
let gatekeep = documents.filter(d => {
1083+
// Each item must be valid JSON, but can't be an array.
10761084
if(Array.isArray(d) || typeof d !== "object") return d
1085+
try {
1086+
JSON.parse(JSON.stringify(d))
1087+
} catch (err) {
1088+
return d
1089+
}
1090+
// Items must have an @id, or in some cases an id will do
10771091
const idcheck = _contextid(d["@context"]) ? d.id ?? d["@id"] : d["@id"]
10781092
if(!idcheck) return d
10791093
})
1080-
if (gate.length > 1) {
1081-
err.message = "All objects in the body of a `/bulkUpdate` must be JSON and must contain an @id or id property."
1094+
if (gatekeep.length > 1) {
1095+
err.message = "All objects in the body of a `/bulkUpdate` must be JSON and must contain a declared identifier property."
10821096
err.status = 400
10831097
next(createExpressError(err))
10841098
return
@@ -1091,46 +1105,44 @@ const bulkUpdate = async function (req, res, next) {
10911105
// but we will not update the same thing twice.
10921106
if(encountered.includes(idReceived)) continue
10931107
encountered.push(idReceived)
1094-
if (idReceived) {
1095-
if(!idReceived.includes(process.env.RERUM_ID_PREFIX)){
1096-
continue
1097-
}
1098-
let id = parseDocumentID(idReceived)
1099-
let originalObject
1100-
try {
1101-
originalObject = await db.findOne({"$or":[{"_id": id}, {"__rerum.slug": id}]})
1102-
} catch (error) {
1103-
next(createExpressError(error))
1104-
return
1105-
}
1106-
if (null === originalObject) {
1107-
continue
1108-
}
1109-
if (utils.isDeleted(originalObject)) {
1110-
continue
1111-
}
1112-
id = ObjectID()
1113-
let context = objectReceived["@context"] ? { "@context": objectReceived["@context"] } : {}
1114-
let rerumProp = { "__rerum": utils.configureRerumOptions(generatorAgent, originalObject, true, false)["__rerum"] }
1115-
delete objectReceived["__rerum"]
1116-
delete objectReceived["_id"]
1117-
delete objectReceived["@id"]
1118-
// id is also protected in this case, so it can't be set.
1119-
if(_contextid(objectReceived["@context"])) delete objectReceived.id
1120-
delete objectReceived["@context"]
1121-
let newObject = Object.assign(context, { "@id": process.env.RERUM_ID_PREFIX + id }, objectReceived, rerumProp, { "_id": id })
1122-
bulkOps.push({ insertOne : { "document" : newObject }})
1123-
if(originalObject.__rerum.history.next.indexOf(newObject["@id"]) === -1){
1124-
originalObject.__rerum.history.next.push(newObject["@id"])
1125-
const replaceOp = { replaceOne :
1126-
{
1127-
"filter" : { "_id": originalObject["_id"] },
1128-
"replacement" : originalObject,
1129-
"upsert" : false
1130-
}
1108+
if(!idReceived.includes(process.env.RERUM_ID_PREFIX)){
1109+
continue
1110+
}
1111+
let id = parseDocumentID(idReceived)
1112+
let originalObject
1113+
try {
1114+
originalObject = await db.findOne({"$or":[{"_id": id}, {"__rerum.slug": id}]})
1115+
} catch (error) {
1116+
next(createExpressError(error))
1117+
return
1118+
}
1119+
if (null === originalObject) {
1120+
continue
1121+
}
1122+
if (utils.isDeleted(originalObject)) {
1123+
continue
1124+
}
1125+
id = ObjectID()
1126+
let context = objectReceived["@context"] ? { "@context": objectReceived["@context"] } : {}
1127+
let rerumProp = { "__rerum": utils.configureRerumOptions(generatorAgent, originalObject, true, false)["__rerum"] }
1128+
delete objectReceived["__rerum"]
1129+
delete objectReceived["_id"]
1130+
delete objectReceived["@id"]
1131+
// id is also protected in this case, so it can't be set.
1132+
if(_contextid(objectReceived["@context"])) delete objectReceived.id
1133+
delete objectReceived["@context"]
1134+
let newObject = Object.assign(context, { "@id": process.env.RERUM_ID_PREFIX + id }, objectReceived, rerumProp, { "_id": id })
1135+
bulkOps.push({ insertOne : { "document" : newObject }})
1136+
if(originalObject.__rerum.history.next.indexOf(newObject["@id"]) === -1){
1137+
originalObject.__rerum.history.next.push(newObject["@id"])
1138+
const replaceOp = { replaceOne :
1139+
{
1140+
"filter" : { "_id": originalObject["_id"] },
1141+
"replacement" : originalObject,
1142+
"upsert" : false
11311143
}
1132-
bulkOps.push(replaceOp)
11331144
}
1145+
bulkOps.push(replaceOp)
11341146
}
11351147
}
11361148
try {

0 commit comments

Comments
 (0)