Inlined.add registry - #2961
Conversation
Wiz Scan Summary
To detect these findings earlier in the dev lifecycle, try the Wiz Code extension for VS Code, JetBrains, or Visual Studio. |
There was a problem hiding this comment.
Code Review
This pull request migrates several Firebase Extensions into npm-packaged, deployable Firebase Functions (v2) under the kits/ directory, while adding deprecation notices to the legacy extension directories. The review identified several critical robustness and logic issues across the new packages: a potential runtime crash in bigquery-firestore-export when parsing raw BigQueryTime values; a potential crash in firestore-send-email when calling db.getAll() with an empty array; a schema parsing bug in firestore-genai-chatbot that incorrectly converts valid 0 values to undefined; fragile direct comparison of Firestore Timestamp objects; and unhandled optional parameters in firestore-bundle-builder that could lead to NaN or TypeError exceptions.
I am having trouble creating individual review comments. Click here to see my feedback.
kits/firestore-bundle-builder/src/build-bundle.ts (95-115)
The TODO comment correctly identifies a robustness issue. If an optional parameter is not provided, paramValues[p] will be undefined, causing parseInt(undefined) to return NaN or throwing a TypeError when calling .map on array types. Adding a guard for undefined and ensuring array types are wrapped correctly prevents runtime crashes and ensures correct fallback behavior.
if (typeof paramValues[p] === "undefined") {
return undefined;
}
switch (pOpts.type || "string") {
case "integer":
return parseInt(paramValues[p], 10);
case "float":
return parseFloat(paramValues[p]);
case "boolean":
return paramValues[p] === "true";
case "integer-array": {
const arr = Array.isArray(paramValues[p]) ? paramValues[p] : [paramValues[p]];
return arr.map((s) => parseInt(s, 10));
}
case "float-array": {
const arr = Array.isArray(paramValues[p]) ? paramValues[p] : [paramValues[p]];
return arr.map((s) => parseFloat(s));
}
case "string":
return paramValues[p];
case "string-array":
return Array.isArray(paramValues[p]) ? paramValues[p] : [paramValues[p]];
}kits/firestore-genai-chatbot/src/firestore.ts (48-60)
Comparing Firestore Timestamp objects directly using the < operator is fragile and unreliable in JavaScript because valueOf() is not overridden to return a comparable primitive (like milliseconds). This can cause the history filter to fail or behave unexpectedly. Implementing a safe comparison helper that duck-types toMillis() or compares Date objects correctly ensures robust history ordering.
const compareValues = (a: any, b: any): boolean => {
if (a && typeof a.toMillis === "function" && b && typeof b.toMillis === "function") {
return a.toMillis() < b.toMillis();
}
if (a instanceof Date && b instanceof Date) {
return a.getTime() < b.getTime();
}
return a < b;
};
return collSnap.docs
.filter((snap) => {
const val = snap.get(orderField);
return val && compareValues(val, refOrderFieldVal);
})kits/bigquery-firestore-export/src/helper.ts (181-188)
Passing a raw BigQueryTime value (e.g., "12:30:00") directly to new Date() results in Invalid Date in JavaScript, which will cause Timestamp.fromDate to throw a runtime error and crash the export function. Prepending a dummy date string for BigQueryTime instances ensures successful parsing.
if (
row instanceof BigQueryTimestamp ||
row instanceof BigQueryDate ||
row instanceof BigQueryTime ||
row instanceof BigQueryDatetime
) {
let dateValue = row.value;
if (row instanceof BigQueryTime) {
dateValue = `1970-01-01T${row.value}Z`;
}
const parsedDate = new Date(dateValue);
return Timestamp.fromDate(isNaN(parsedDate.getTime()) ? new Date(0) : parsedDate);
}kits/firestore-send-email/src/prepare-payload.ts (132-144)
If payload.toUids is defined as an empty array [], uids will be empty, and db.getAll() will be called with no document references. In the Firestore Node.js SDK, calling getAll() without any document references throws a runtime error. Adding a guard to return early when uids is empty prevents this crash.
if (uids.length === 0) {
payload.to = to;
payload.cc = cc;
payload.bcc = bcc;
return payload;
}
const toFetch: Record<string, string | null> = {};
uids.forEach((uid) => {
toFetch[uid] = null;
});
const documents = await db.getAll(
...Object.keys(toFetch).map((uid) =>
db.collection(config.usersCollection as string).doc(uid)
),
{ fieldMask: ["email"] }
);kits/firestore-genai-chatbot/src/overrides.ts (21-25)
The current intSchema transform converts 0 to undefined because parseInt("0", 10) || undefined evaluates to undefined. This prevents users from setting valid integer values of 0 (e.g., for temperature or other parameters). Additionally, floatSchema does not handle NaN resulting from empty strings, which can cause downstream API failures. Checking isNaN explicitly solves both issues.
const intSchema = z.union([
z.string().transform((arg) => {
const parsed = parseInt(arg, 10);
return isNaN(parsed) ? undefined : parsed;
}),
z.number(),
]);
const floatSchema = z.union([
z.string().transform((arg) => {
const parsed = parseFloat(arg);
return isNaN(parsed) ? undefined : parsed;
}),
z.number(),
]);kits/firestore-translate-text/src/translate/translateMultiple.ts (29-42)
Calling logs.translateInputStringToAllLanguages inside the nested languages.forEach loop is redundant and misleading, as it logs a message claiming to translate to all languages multiple times for each individual language task. Moving it outside the inner loop ensures accurate and clean log output.
Object.entries(input).forEach(([inputKey, value]) => {
logs.translateInputStringToAllLanguages(String(value), [...languages]);
languages.forEach((language) => {
tasks.push(async () => {
const output =
typeof value === "string"
? await service.translateString(value, language)
: null;
if (!translations[inputKey]) translations[inputKey] = {};
translations[inputKey][language] = output;
});
});
});
No description provided.