Skip to content

Neutralize formula characters in csv exports#4128

Open
pepeladeira wants to merge 5 commits into
mainfrom
csv-export-formula-injection
Open

Neutralize formula characters in csv exports#4128
pepeladeira wants to merge 5 commits into
mainfrom
csv-export-formula-injection

Conversation

@pepeladeira

@pepeladeira pepeladeira commented Jul 4, 2026

Copy link
Copy Markdown
Collaborator

Summary by CodeRabbit

  • Bug Fixes
    • Improved partner profile and onboarding input handling to reduce spreadsheet/CSV formula injection risk.
    • Partner text fields (name, description, and company name) are now sanitized to remove formula-style leading characters.
    • Onboarding continues to preserve existing defaults (such as profile type and image handling) while applying the new sanitization consistently.

@vercel

vercel Bot commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
dub Ready Ready Preview Jul 19, 2026 5:02pm

Request Review

@coderabbitai

coderabbitai Bot commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 60c0c9d5-147d-4dee-abc6-78d15502187c

📥 Commits

Reviewing files that changed from the base of the PR and between 960ae9a and 9444a0e.

📒 Files selected for processing (2)
  • apps/web/lib/actions/partners/update-partner-profile.ts
  • apps/web/lib/zod/schemas/partners.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/web/lib/actions/partners/update-partner-profile.ts
  • apps/web/lib/zod/schemas/partners.ts

📝 Walkthrough

Walkthrough

A shared sanitizeFormulaInput helper strips leading formula-trigger characters and leading whitespace from partner text inputs. It is applied to name, description, and companyName in both onboarding and partner profile update Zod schemas.

Changes

Partner Field Sanitization

Layer / File(s) Summary
sanitizeFormulaInput helper and onboarding schema
apps/web/lib/zod/schemas/partners.ts
Adds the exported helper and applies it to name, description, and companyName in onboardPartnerSchema; image still uses partnerImageSchema, and profileType still defaults to "individual".
Update-profile action wiring
apps/web/lib/actions/partners/update-partner-profile.ts
Imports the helper and applies it to name, description, and companyName in updatePartnerProfileSchema, preserving the existing validation rules.

Estimated code review effort: 2 (Simple) | ~10 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Title check ⚠️ Warning The title mentions CSV exports, but the changes sanitize partner profile input fields in partner schemas instead. Rename it to describe the actual change, such as sanitizing formula-like input in partner profile schemas.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch csv-export-formula-injection

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.

@pepeladeira

Copy link
Copy Markdown
Collaborator Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Jul 4, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Full review finished.

@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: 3

🤖 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 `@apps/web/lib/actions/partners/update-partner-profile.ts`:
- Around line 33-38: The partner profile schema’s name validation has the same
required-then-sanitized ordering problem as the onboarding schema, so a
formula-only input can become empty after sanitization and overwrite the
existing value. Update the `update-partner-profile.ts` schema `name` field (and
align it with `onboardPartnerSchema.name` if shared) so `sanitizeFormulaInput`
runs before enforcing non-empty content, or add a post-transform
`refine`/equivalent check after `sanitizeFormulaInput` to reject empty results
while preserving the optional behavior.

In `@apps/web/lib/zod/schemas/partners.ts`:
- Around line 752-760: The sanitizeFormulaInput helper currently removes formula
prefixes before leading whitespace, so values like “ =...” can still end up
looking like formulas. Update sanitizeFormulaInput to strip leading whitespace
first or include it in the same leading removal, then ensure the name,
description, and companyName transforms in partners.ts use this helper so all
three fields get the corrected sanitization.
- Line 770: The name field validation in the partners schema allows values that
become empty after sanitizeFormulaInput because z.string().min(1) runs before
the transform. Update the name pipeline in the partners schema to revalidate
after sanitizeFormulaInput, matching the companyName pattern, so the sanitized
result must still be non-empty.
🪄 Autofix (Beta)

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

Run ID: ac186c41-29c9-4db4-83dc-8a2bcc8dbd75

📥 Commits

Reviewing files that changed from the base of the PR and between 57bc286 and 604a09c.

📒 Files selected for processing (2)
  • apps/web/lib/actions/partners/update-partner-profile.ts
  • apps/web/lib/zod/schemas/partners.ts

Comment thread apps/web/lib/actions/partners/update-partner-profile.ts Outdated
Comment thread apps/web/lib/zod/schemas/partners.ts
Comment thread apps/web/lib/zod/schemas/partners.ts Outdated

@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.

♻️ Duplicate comments (3)
apps/web/lib/zod/schemas/partners.ts (2)

752-760: 🔒 Security & Privacy | 🔴 Critical | ⚡ Quick win

Formula-char strip still bypassable via leading whitespace.

replace(/^[=+\-@\t\r]+/, "") only matches at index 0 and the class excludes plain space/newline, so a leading space blocks the match; .trimStart() then removes that space and re-exposes the formula trigger (" =cmd|calc!A0""=cmd|calc!A0"). This is the exact bug flagged in a prior review on this file and marked "Addressed", but the code here is unchanged from the vulnerable version — description and companyName (which don't get a .trim() upstream) remain exploitable, defeating the purpose of this PR.

🐛 Proposed fix
-  return value.replace(/^[=+\-@\t\r]+/, "").trimStart() as T;
+  return value.replace(/^[\s=+\-@]+/, "") as T;

Merging whitespace into the same leading character class strips interleaved whitespace/formula-char runs (e.g. " = A1") in one pass instead of two ordered passes.

🤖 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 `@apps/web/lib/zod/schemas/partners.ts` around lines 752 - 760, The
sanitizeFormulaInput helper still allows a leading space/newline to hide a
formula trigger because the regex only strips characters at index 0 and
trimStart runs afterward. Update sanitizeFormulaInput in partners.ts so the
leading-stripping step handles whitespace and formula-leading characters
together in one pass, and keep the behavior applied to fields like description
and companyName that do not get trimmed upstream.

770-770: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Revalidate name after sanitizing.

z.string().min(1) runs before .transform(sanitizeFormulaInput), so a submission like "=" passes the required check and is then sanitized to "", silently bypassing the "Name is required" constraint. This matches a still-open concern from a prior review pass on this exact line.

🐛 Proposed fix
-    name: z.string().min(1, "Name is required").transform(sanitizeFormulaInput),
+    name: z
+      .string()
+      .min(1, "Name is required")
+      .transform(sanitizeFormulaInput)
+      .refine((v) => v.length > 0, { message: "Name is required" }),
🤖 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 `@apps/web/lib/zod/schemas/partners.ts` at line 770, The `name` field in the
`partners` Zod schema is only validated before `sanitizeFormulaInput` runs, so a
value like "=" can become empty after transformation and bypass the required
check. Update the `name` pipeline in the partners schema to validate again after
`sanitizeFormulaInput` (for example by moving the required validation to the
sanitized result or adding a post-transform refinement) so the final value
cannot be empty. Use the `name` schema entry in `partners.ts` as the location to
adjust.
apps/web/lib/actions/partners/update-partner-profile.ts (1)

33-38: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Same required-then-emptied ordering issue as onboarding schema's name field.

.min(1, "Name is required") runs before .transform(sanitizeFormulaInput). A submitted name of purely formula-trigger characters (e.g. "=") passes the required check, then sanitizes to "", silently overwriting the partner's existing name. Same class of issue as apps/web/lib/zod/schemas/partners.ts's onboardPartnerSchema.name, previously flagged here and still unresolved.

🐛 Proposed fix
     name: z
       .string()
       .trim()
       .min(1, "Name is required")
       .optional()
-      .transform(sanitizeFormulaInput),
+      .transform(sanitizeFormulaInput)
+      .refine((v) => v === undefined || v.length > 0, {
+        message: "Name is required",
+      }),
🤖 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 `@apps/web/lib/actions/partners/update-partner-profile.ts` around lines 33 -
38, The partner profile `name` schema in `update-partner-profile.ts` has the
same required-then-sanitized ordering bug as `onboardPartnerSchema.name`: the
`z.string().trim().min(1, "Name is
required").optional().transform(sanitizeFormulaInput)` chain validates before
sanitizing, so formula-only input can become empty after transform and overwrite
the existing name. Update this field so `sanitizeFormulaInput` runs before the
non-empty validation, matching the fix pattern used for the onboarding schema,
and keep the `name` field in sync with the corresponding partner schema
definitions.
🤖 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.

Duplicate comments:
In `@apps/web/lib/actions/partners/update-partner-profile.ts`:
- Around line 33-38: The partner profile `name` schema in
`update-partner-profile.ts` has the same required-then-sanitized ordering bug as
`onboardPartnerSchema.name`: the `z.string().trim().min(1, "Name is
required").optional().transform(sanitizeFormulaInput)` chain validates before
sanitizing, so formula-only input can become empty after transform and overwrite
the existing name. Update this field so `sanitizeFormulaInput` runs before the
non-empty validation, matching the fix pattern used for the onboarding schema,
and keep the `name` field in sync with the corresponding partner schema
definitions.

In `@apps/web/lib/zod/schemas/partners.ts`:
- Around line 752-760: The sanitizeFormulaInput helper still allows a leading
space/newline to hide a formula trigger because the regex only strips characters
at index 0 and trimStart runs afterward. Update sanitizeFormulaInput in
partners.ts so the leading-stripping step handles whitespace and formula-leading
characters together in one pass, and keep the behavior applied to fields like
description and companyName that do not get trimmed upstream.
- Line 770: The `name` field in the `partners` Zod schema is only validated
before `sanitizeFormulaInput` runs, so a value like "=" can become empty after
transformation and bypass the required check. Update the `name` pipeline in the
partners schema to validate again after `sanitizeFormulaInput` (for example by
moving the required validation to the sanitized result or adding a
post-transform refinement) so the final value cannot be empty. Use the `name`
schema entry in `partners.ts` as the location to adjust.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: cdf4a93b-2563-4173-82e4-c1b6ddc3e97d

📥 Commits

Reviewing files that changed from the base of the PR and between 91ff0db and 604a09c.

📒 Files selected for processing (2)
  • apps/web/lib/actions/partners/update-partner-profile.ts
  • apps/web/lib/zod/schemas/partners.ts

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.

2 participants