Skip to content

fix: add client-side validation for Firestore writes (#154) - #768

Open
saurabhhhcodes wants to merge 1 commit into
roshankumar0036singh:mainfrom
saurabhhhcodes:fix/154-input-validation
Open

fix: add client-side validation for Firestore writes (#154)#768
saurabhhhcodes wants to merge 1 commit into
roshankumar0036singh:mainfrom
saurabhhhcodes:fix/154-input-validation

Conversation

@saurabhhhcodes

@saurabhhhcodes saurabhhhcodes commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Closes #154

Problem

Event and profile payloads were written to Firestore with no client-side validation — invalid types, oversized strings, and malformed links only failed at the rules layer with generic permission errors (or worse, partially corrupted data).

Changes

  • app/src/lib/validators.js (new, no new dependencies): validators that mirror the existing server-side rules (validateEventShape in firestore.rules):
    • validateEventInput — title (3-200 chars), required description (max 5000), ISO date + end-after-start ordering, price (0-100000), capacity (1-10000), https-only meet/registration/banner URLs
    • validateProfileInput — display name length, bio cap, instagram handle charset, https-only LinkedIn
    • returns { valid, errors } for friendly messages
  • app/src/screens/CreateEvent.js: event payload validated before both create and update writes; invalid input shows an alert listing every problem instead of writing.
  • app/src/lib/__tests__/validators.test.js: 9 tests — valid payloads, short/long titles, missing/oversized descriptions, invalid date order, price/capacity bounds, http/javascript links rejected, null/optional values allowed.

Requests the same protection for user/profile writes as a follow-up once the event path lands.

Summary by CodeRabbit

  • New Features

    • Added validation for event details, including required fields, text lengths, dates, prices, capacity, and HTTPS links.
    • Added validation support for profile information such as display names, bios, social handles, and LinkedIn URLs.
    • Invalid event submissions now show clear validation errors and are not saved.
  • Tests

    • Added comprehensive coverage for valid and invalid event, profile, and URL inputs.

…singh#154)

Event and profile payloads were written straight to Firestore with no
client-side checks, so invalid types, oversized strings and malformed
links only failed (or corrupted data) at the rules layer with generic
permission errors.

- lib/validators.js: dependency-free validators mirroring the server
  rules (validateEventShape): title/description length limits, ISO date
  ordering, price/capacity bounds, https-only URLs; validateEventInput
  and validateProfileInput return { valid, errors }
- CreateEvent.js: event payload validated before the create/update
  write, with a friendly error alert listing all problems
- 9 tests covering valid/invalid payloads, types, bounds and URL rules
@sonarqubecloud

Copy link
Copy Markdown

@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Changes

Input validation

Layer / File(s) Summary
Validation rules and coverage
app/src/lib/validators.js, app/src/lib/__tests__/validators.test.js
Adds shared limits, primitive validators, event and profile validation, HTTPS URL checks, and Jest coverage for valid and invalid inputs.
Event submission guard
app/src/screens/CreateEvent.js
Validates the assembled event payload before persistence. Invalid input displays an alert, resets loading, and stops submission.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant CreateEvent
  participant validateEventInput
  participant Firestore
  CreateEvent->>validateEventInput: validate assembled event payload
  alt invalid payload
    validateEventInput-->>CreateEvent: return validation errors
    CreateEvent-->>CreateEvent: show Invalid Input alert
  else valid payload
    validateEventInput-->>CreateEvent: return valid
    CreateEvent->>Firestore: update or create event
  end
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The PR implements event and profile validation, but issue #154 also requires validation and write protection for user, club, attendance, and all Firestore writes. Extend validation and write protection to user, club, attendance, and all relevant Firestore operations, or narrow issue #154 scope.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the main change: adding client-side validation before Firestore writes.
Out of Scope Changes check ✅ Passed The validator utilities, profile checks, tests, and CreateEvent integration support the stated validation objectives and do not introduce unrelated changes.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

app/src/lib/__tests__/validators.test.js

ESLint skipped: missing config or dependency (missing-dependency). The ESLint configuration references a package that is not available in the sandbox.

app/src/lib/validators.js

ESLint skipped: the ESLint configuration for this file references a package that is not available in the sandbox.

app/src/screens/CreateEvent.js

ESLint skipped: the ESLint configuration for this file references a package that is not available in the sandbox.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@app/src/lib/__tests__/validators.test.js`:
- Line 1: Update the import statement in the validator tests to import
validateEventInput, validateProfileInput, and isHttpUrl from the parent
directory using ../validators instead of ./validators.

In `@app/src/lib/validators.js`:
- Around line 69-72: Update the capacity validation condition in the visible
validator to also require Number.isInteger(data.capacity), preserving the
existing range and finite-number checks. Add or update the validator tests to
verify that a decimal capacity such as 1.5 is rejected.
- Around line 103-115: Update the optional bio and Instagram validation checks
in the profile validator to require string values before accessing bio.length or
applying the Instagram pattern. Ensure null and other non-string values are
rejected with the appropriate existing validation errors, while valid strings
retain the current length and format rules.
- Around line 38-90: Reduce cognitive complexity in validateEventInput by
extracting the date, numeric, and link validation branches into focused helper
functions, then append their errors in the same order currently produced.
Preserve all existing validation rules, messages, and ordering while keeping
validateEventInput responsible for combining the helper results.
- Around line 81-87: Update the bannerUrl validation condition in the relevant
validator to rely solely on !isHttpUrl(data.bannerUrl), removing the
/^https?:\/\// special case so HTTP URLs are rejected while valid URLs continue
through the existing error handling.
- Around line 30-31: Update isHttpUrl to parse the value with the URL parser and
reject parsing failures, non-https protocols, or empty hostnames while retaining
the existing string and maxLength checks. Add a regression test covering the
malformed value “https://?” and ensure it is rejected.

In `@app/src/screens/CreateEvent.js`:
- Around line 482-487: Move or add a preflight call to validateEventInput before
uploadImage() and optional Meet-link generation, using the form-controlled event
fields available at that stage; show the existing invalid-input alert, clear
loading, and return on failure. Retain the current validation after derived
values are added so the final eventData remains validated before persistence.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: c540625e-8089-4f58-add0-1d6c8f0f7483

📥 Commits

Reviewing files that changed from the base of the PR and between 5e6135e and d336612.

📒 Files selected for processing (3)
  • app/src/lib/__tests__/validators.test.js
  • app/src/lib/validators.js
  • app/src/screens/CreateEvent.js

@@ -0,0 +1,91 @@
import { validateEventInput, validateProfileInput, isHttpUrl } from './validators';

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# List validator modules under the relevant library directory.
fd -a -t f '^validators\.js$' app/src/lib

Repository: roshankumar0036singh/Uni-Event

Length of output: 213


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- test directory files ---'
find app/src/lib/__tests__ -maxdepth 1 -type f -printf '%f\n' | sort

printf '%s\n' '--- import and validator references ---'
rg -n --no-heading "from ['\"]\\.?\\.?/validators|require\\(['\"]\\.?\\.?/validators|validateEventInput|validateProfileInput|isHttpUrl" \
  app/src/lib/__tests__ app/src/lib/validators.js

printf '%s\n' '--- relevant test file ---'
cat -n app/src/lib/__tests__/validators.test.js | sed -n '1,80p'

printf '%s\n' '--- nearby validator paths ---'
find app/src/lib -maxdepth 3 \( -type f -o -type l \) -iname 'validators*' -print | sort

Repository: roshankumar0036singh/Uni-Event

Length of output: 8341


Import the validator module from its parent directory.

./validators resolves to the test directory, where no sibling module exists. Use ../validators.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/src/lib/__tests__/validators.test.js` at line 1, Update the import
statement in the validator tests to import validateEventInput,
validateProfileInput, and isHttpUrl from the parent directory using
../validators instead of ./validators.

Comment thread app/src/lib/validators.js
Comment on lines +30 to +31
export const isHttpUrl = (value, maxLength = LIMITS.URL_MAX) =>
typeof value === 'string' && value.length <= maxLength && /^https:\/\/.+/.test(value);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect URL API/polyfill usage before adopting a parser-based implementation.
fd -a -t f '^package\.json$' . -x sh -c 'echo "--- $1"; rg -n "react-native-url-polyfill|whatwg-url|\\bURL\\b" "$1" || true' sh {}
rg -n -C 2 'isHttpUrl|new URL|react-native-url-polyfill|whatwg-url' app/src

Repository: roshankumar0036singh/Uni-Event

Length of output: 2976


🏁 Script executed:

#!/bin/bash
set -euo pipefail

for f in $(fd -a -t f '^package\.json$' .); do
  echo "--- $f"
  cat "$f"
done

echo "--- app/src/lib/validators.js"
cat -n app/src/lib/validators.js

echo "--- app/src/lib/__tests__/validators.test.js"
cat -n app/src/lib/__tests__/validators.test.js

echo "--- runtime/config references"
rg -n -C 2 'react-native|expo|engines|global\.URL|new URL|URL\(' --glob '!**/node_modules/**' --glob '!**/dist/**' .

Repository: roshankumar0036singh/Uni-Event

Length of output: 50387


🏁 Script executed:

node - <<'JS'
const values = ['https://?', 'https://', 'https://.', 'https://example.com/a'];
const re = /^https:\/\/.+/;
for (const value of values) {
  let parsed;
  try {
    parsed = new URL(value);
  } catch {
    parsed = null;
  }
  console.log(JSON.stringify({
    value,
    currentValidator: re.test(value),
    parsedProtocol: parsed?.protocol ?? null,
    parsedHostname: parsed?.hostname ?? null,
    parserAccepts: Boolean(parsed && parsed.protocol === 'https:' && parsed.hostname),
  }));
}
console.log({globalURLType: typeof URL});
JS

Repository: roshankumar0036singh/Uni-Event

Length of output: 672


🌐 Web query:

React Native 0.76.9 global URL API Hermes URL constructor supported

💡 Result:

Yes, in React Native 0.76.9, the global URL API is supported in the Hermes JavaScript engine [1]. React Native provides a built-in implementation of the URL and URLSearchParams classes that is available globally [1]. While older versions of React Native had a partially implemented URL class with missing methods and properties, version 0.76.9 includes updates that added standard properties (such as hash, host, hostname, and pathname) and methods (such as get, set, and delete) [1]. Key points regarding the current status: - Native Availability: The URL constructor is exposed globally within the React Native environment, including when using Hermes [2][1]. - Updates in 0.76.9: The 0.76.9 release includes specific improvements to the URL class, such as adding missing standard properties and fixing type definitions for the constructor [1]. - Known Limitations: Despite ongoing improvements, the native implementation is often described as a lightweight, partial polyfill rather than a full, spec-compliant implementation of the WHATWG URL standard [3]. Users may still encounter edge cases or minor behavioral differences compared to web browser implementations (e.g., specific issues with relative path resolution or trailing slashes in some scenarios) [4][5]. - Alternatives: If you require a strictly spec-compliant or more robust implementation, community-maintained polyfills like react-native-url-polyfill remain available and compatible with Hermes [3]. In summary, the global URL API is functional and significantly more complete in React Native 0.76.9 than in previous releases, making it suitable for most standard use cases [1].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
import json
from pathlib import Path

for path in Path('.').rglob('package.json'):
    if any(part in {'node_modules', 'dist', 'build'} for part in path.parts):
        continue
    data = json.loads(path.read_text())
    print(f'--- {path}')
    for section in ('engines', 'dependencies', 'devDependencies', 'peerDependencies'):
        values = data.get(section, {})
        if section == 'engines':
            print(section, values)
        else:
            print(section, {k: v for k, v in values.items()
                            if 'url' in k.lower() or 'react-native' in k.lower()
                            or k in {'react', 'expo', 'hermes-engine'}})
PY

echo "--- URL-related lockfile package names"
rg -n '"(react-native-url-polyfill|whatwg-url|url|url-parse|react-native)"' app/package-lock.json package-lock.json 2>/dev/null | head -80 || true

echo "--- focused URL references outside generated/dependency files"
rg -n -C 2 'isHttpUrl|new URL|react-native-url-polyfill|whatwg-url|global\.URL' app/src app/App.js app/index.js app/package.json app/babel.config.js app/metro.config.js 2>/dev/null || true

Repository: roshankumar0036singh/Uni-Event

Length of output: 10723


Parse the URL before accepting it. Reject values when parsing fails, the protocol is not https:, or the hostname is empty. The current regex accepts malformed values such as https://?; add a regression test for this case.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/src/lib/validators.js` around lines 30 - 31, Update isHttpUrl to parse
the value with the URL parser and reject parsing failures, non-https protocols,
or empty hostnames while retaining the existing string and maxLength checks. Add
a regression test covering the malformed value “https://?” and ensure it is
rejected.

Comment thread app/src/lib/validators.js
Comment on lines +38 to +90
export const validateEventInput = (data = {}) => {
const errors = [];

if (!isStringOfLength(data.title, LIMITS.TITLE_MIN, LIMITS.TITLE_MAX)) {
errors.push(
`Title must be between ${LIMITS.TITLE_MIN} and ${LIMITS.TITLE_MAX} characters.`,
);
}

if (!isNonEmptyString(data.description) || data.description.length > LIMITS.DESCRIPTION_MAX) {
errors.push(
`Description is required and must not exceed ${LIMITS.DESCRIPTION_MAX} characters.`,
);
}

if (!isIsoDateString(data.startAt)) {
errors.push('Start date must be a valid date.');
}
if (!isIsoDateString(data.endAt)) {
errors.push('End date must be a valid date.');
}
if (isIsoDateString(data.startAt) && isIsoDateString(data.endAt)) {
if (new Date(data.endAt) <= new Date(data.startAt)) {
errors.push('End date must be after the start date.');
}
}

if (!isFiniteNumber(data.price, 0, LIMITS.PRICE_MAX)) {
errors.push(`Price must be a number between 0 and ${LIMITS.PRICE_MAX}.`);
}

if (data.capacity !== null && data.capacity !== undefined) {
if (!isFiniteNumber(data.capacity, 1, LIMITS.CAPACITY_MAX)) {
errors.push(`Capacity must be a whole number between 1 and ${LIMITS.CAPACITY_MAX}.`);
}
}

if (data.meetLink && !isHttpUrl(data.meetLink)) {
errors.push('Meet link must be an https:// URL.');
}
if (data.registrationLink && !isHttpUrl(data.registrationLink)) {
errors.push('Registration link must be an https:// URL.');
}
if (
data.bannerUrl &&
!isHttpUrl(data.bannerUrl) &&
/^https?:\/\//.test(data.bannerUrl) === false
) {
errors.push('Banner URL must be an https:// URL.');
}

return { valid: errors.length === 0, errors };
};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Reduce validateEventInput complexity.

SonarCloud reports cognitive complexity 19, but the configured maximum is 15. Extract date, numeric, and link checks into focused helpers. Preserve the current error order.

🧰 Tools
🪛 GitHub Check: SonarCloud Code Analysis

[failure] 38-38: Refactor this function to reduce its Cognitive Complexity from 19 to the 15 allowed.

See more on https://sonarcloud.io/project/issues?id=roshankumar0036singh_Uni-Event&issues=AZ_1QuL9ZXv_7V44MmxH&open=AZ_1QuL9ZXv_7V44MmxH&pullRequest=768

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/src/lib/validators.js` around lines 38 - 90, Reduce cognitive complexity
in validateEventInput by extracting the date, numeric, and link validation
branches into focused helper functions, then append their errors in the same
order currently produced. Preserve all existing validation rules, messages, and
ordering while keeping validateEventInput responsible for combining the helper
results.

Source: Linters/SAST tools

Comment thread app/src/lib/validators.js
Comment on lines +69 to +72
if (data.capacity !== null && data.capacity !== undefined) {
if (!isFiniteNumber(data.capacity, 1, LIMITS.CAPACITY_MAX)) {
errors.push(`Capacity must be a whole number between 1 and ${LIMITS.CAPACITY_MAX}.`);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Require an integer capacity.

isFiniteNumber(1.5, 1, LIMITS.CAPACITY_MAX) returns true. This permits fractional capacities although the error message requires a whole number. Add Number.isInteger(data.capacity) and cover a decimal capacity in the tests.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/src/lib/validators.js` around lines 69 - 72, Update the capacity
validation condition in the visible validator to also require
Number.isInteger(data.capacity), preserving the existing range and finite-number
checks. Add or update the validator tests to verify that a decimal capacity such
as 1.5 is rejected.

Comment thread app/src/lib/validators.js
Comment on lines +81 to +87
if (
data.bannerUrl &&
!isHttpUrl(data.bannerUrl) &&
/^https?:\/\//.test(data.bannerUrl) === false
) {
errors.push('Banner URL must be an https:// URL.');
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Reject HTTP banner URLs.

The ^https?:\/\/ condition makes http://example.com/banner.png pass validation. Replace this special case with the same !isHttpUrl(data.bannerUrl) check used for the other links.

Proposed fix
-    if (
-        data.bannerUrl &&
-        !isHttpUrl(data.bannerUrl) &&
-        /^https?:\/\//.test(data.bannerUrl) === false
-    ) {
+    if (data.bannerUrl && !isHttpUrl(data.bannerUrl)) {
         errors.push('Banner URL must be an https:// URL.');
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (
data.bannerUrl &&
!isHttpUrl(data.bannerUrl) &&
/^https?:\/\//.test(data.bannerUrl) === false
) {
errors.push('Banner URL must be an https:// URL.');
}
if (data.bannerUrl && !isHttpUrl(data.bannerUrl)) {
errors.push('Banner URL must be an https:// URL.');
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/src/lib/validators.js` around lines 81 - 87, Update the bannerUrl
validation condition in the relevant validator to rely solely on
!isHttpUrl(data.bannerUrl), removing the /^https?:\/\// special case so HTTP
URLs are rejected while valid URLs continue through the existing error handling.

Comment thread app/src/lib/validators.js
Comment on lines +103 to +115
if (data.bio !== undefined && data.bio.length > 600) {
errors.push('Bio must not exceed 600 characters.');
}
if (
data.instagram !== undefined &&
data.instagram &&
!/^[a-zA-Z0-9._]{1,30}$/.test(data.instagram)
) {
errors.push('Instagram handle contains invalid characters.');
}
if (data.linkedin !== undefined && data.linkedin && !isHttpUrl(data.linkedin)) {
errors.push('LinkedIn URL must be an https:// URL.');
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Validate optional profile field types before reading them.

bio: null throws at data.bio.length. Non-string values such as bio: 123 and instagram: 123 can also pass validation. Check for strings before reading .length or applying the handle pattern.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/src/lib/validators.js` around lines 103 - 115, Update the optional bio
and Instagram validation checks in the profile validator to require string
values before accessing bio.length or applying the Instagram pattern. Ensure
null and other non-string values are rejected with the appropriate existing
validation errors, while valid strings retain the current length and format
rules.

Comment on lines +482 to +487
const validation = validateEventInput(eventData);
if (!validation.valid) {
Alert.alert('Invalid Input', validation.errors.join('\n'));
setLoading(false);
return;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Validate user-controlled fields before external side effects.

This validation runs after uploadImage() and optional Meet-link generation. An invalid registration link, title, or capacity can therefore upload a Storage object or create a calendar event before the alert stops the Firestore write.

Run a preflight validation on the form-controlled fields before image upload and Meet generation. Keep this final validation after derived values are added.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/src/screens/CreateEvent.js` around lines 482 - 487, Move or add a
preflight call to validateEventInput before uploadImage() and optional Meet-link
generation, using the form-controlled event fields available at that stage; show
the existing invalid-input alert, clear loading, and return on failure. Retain
the current validation after derived values are added so the final eventData
remains validated before persistence.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Missing input validation for Firestore writes

1 participant