test: add Firestore rules extra-field injection tests (batch v0.2.17)
- Add extra-field injection tests for daily answers, thread answers, messages (create + update), and reactions - Fix existing message update test: use setDoc instead of updateDoc (hasOnly(['text']) requires full replacement) - Fix existing reaction test: remove targetUserId (not in whitelist) - Add seedThread() to reaction allowed test for consistency - 118/118 tests passing
This commit is contained in:
parent
2e2c79be3d
commit
749d3aa6fd
|
|
@ -763,6 +763,21 @@ describe("couples/{coupleId}/question_threads/{threadId}", () => {
|
||||||
});
|
});
|
||||||
await assertFails(getDoc(doc(charlie().firestore(), ANSWER_PATH)));
|
await assertFails(getDoc(doc(charlie().firestore(), ANSWER_PATH)));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("create with extra field is denied", async () => {
|
||||||
|
await seedThread();
|
||||||
|
await assertFails(
|
||||||
|
setDoc(doc(alice().firestore(), ANSWER_PATH), {
|
||||||
|
userId: UID_A,
|
||||||
|
questionId: "q1",
|
||||||
|
answerType: "written",
|
||||||
|
writtenText: CIPHERTEXT,
|
||||||
|
createdAt: serverTimestamp(),
|
||||||
|
updatedAt: serverTimestamp(),
|
||||||
|
maliciousField: "injected",
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// ── messages ───────────────────────────────────────────────────────────────
|
// ── messages ───────────────────────────────────────────────────────────────
|
||||||
|
|
@ -826,11 +841,11 @@ describe("couples/{coupleId}/question_threads/{threadId}", () => {
|
||||||
await testEnv.withSecurityRulesDisabled(async (ctx) => {
|
await testEnv.withSecurityRulesDisabled(async (ctx) => {
|
||||||
await setDoc(doc(ctx.firestore(), msgPath), {
|
await setDoc(doc(ctx.firestore(), msgPath), {
|
||||||
authorUserId: UID_A,
|
authorUserId: UID_A,
|
||||||
text: "Hi",
|
text: CIPHERTEXT,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
await assertSucceeds(
|
await assertSucceeds(
|
||||||
updateDoc(doc(alice().firestore(), msgPath), { text: CIPHERTEXT })
|
setDoc(doc(alice().firestore(), msgPath), { text: CIPHERTEXT })
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -846,6 +861,35 @@ describe("couples/{coupleId}/question_threads/{threadId}", () => {
|
||||||
updateDoc(doc(bob().firestore(), msgPath), { text: "Tampered" })
|
updateDoc(doc(bob().firestore(), msgPath), { text: "Tampered" })
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("create with extra field is denied", async () => {
|
||||||
|
await seedThread();
|
||||||
|
await assertFails(
|
||||||
|
addDoc(collection(alice().firestore(), MSGS_PATH), {
|
||||||
|
authorUserId: UID_A,
|
||||||
|
text: CIPHERTEXT,
|
||||||
|
createdAt: serverTimestamp(),
|
||||||
|
maliciousField: "injected",
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("update with extra field is denied", async () => {
|
||||||
|
const msgPath = `${MSGS_PATH}/extra-field-message`;
|
||||||
|
await seedThread();
|
||||||
|
await testEnv.withSecurityRulesDisabled(async (ctx) => {
|
||||||
|
await setDoc(doc(ctx.firestore(), msgPath), {
|
||||||
|
authorUserId: UID_A,
|
||||||
|
text: CIPHERTEXT,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
await assertFails(
|
||||||
|
updateDoc(doc(alice().firestore(), msgPath), {
|
||||||
|
text: CIPHERTEXT,
|
||||||
|
maliciousField: "injected",
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// ── reactions ──────────────────────────────────────────────────────────────
|
// ── reactions ──────────────────────────────────────────────────────────────
|
||||||
|
|
@ -854,10 +898,10 @@ describe("couples/{coupleId}/question_threads/{threadId}", () => {
|
||||||
const REACTIONS_PATH = `${THREAD_PATH}/reactions`;
|
const REACTIONS_PATH = `${THREAD_PATH}/reactions`;
|
||||||
|
|
||||||
test("member can add reaction with own userId — allowed", async () => {
|
test("member can add reaction with own userId — allowed", async () => {
|
||||||
|
await seedThread();
|
||||||
await assertSucceeds(
|
await assertSucceeds(
|
||||||
setDoc(doc(alice().firestore(), `${REACTIONS_PATH}/${UID_A}_${UID_B}`), {
|
setDoc(doc(alice().firestore(), `${REACTIONS_PATH}/${UID_A}_${UID_B}`), {
|
||||||
userId: UID_A,
|
userId: UID_A,
|
||||||
targetUserId: UID_B,
|
|
||||||
emoji: "❤️",
|
emoji: "❤️",
|
||||||
createdAt: serverTimestamp(),
|
createdAt: serverTimestamp(),
|
||||||
})
|
})
|
||||||
|
|
@ -874,6 +918,18 @@ describe("couples/{coupleId}/question_threads/{threadId}", () => {
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("create with extra field is denied", async () => {
|
||||||
|
await seedThread();
|
||||||
|
await assertFails(
|
||||||
|
setDoc(doc(alice().firestore(), `${REACTIONS_PATH}/${UID_A}_${UID_B}`), {
|
||||||
|
userId: UID_A,
|
||||||
|
emoji: "❤️",
|
||||||
|
createdAt: serverTimestamp(),
|
||||||
|
maliciousField: "injected",
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -1155,6 +1211,20 @@ describe("couples/{coupleId}/daily_question/{date}", () => {
|
||||||
});
|
});
|
||||||
await assertFails(deleteDoc(doc(alice().firestore(), ANSWER_PATH)));
|
await assertFails(deleteDoc(doc(alice().firestore(), ANSWER_PATH)));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("create with extra field is denied", async () => {
|
||||||
|
await assertFails(
|
||||||
|
setDoc(doc(alice().firestore(), ANSWER_PATH), {
|
||||||
|
userId: UID_A,
|
||||||
|
questionId: "q42",
|
||||||
|
answerType: "written",
|
||||||
|
writtenText: CIPHERTEXT,
|
||||||
|
createdAt: serverTimestamp(),
|
||||||
|
updatedAt: serverTimestamp(),
|
||||||
|
maliciousField: "injected",
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue