- Location
- scripts/cryptofolio.mjs:568
- Finding
- Spreadsheet Formula Injection in Exported Portfolio Reports<![CDATA[
## Vulnerability Details
**File Location**: `scripts/cryptofolio.mjs:568-619`
**Vulnerability Type**: CSV formula injection and incomplete CSV escaping
**Risk Level**: Medium
### Vulnerable Code
```js
// 账户汇总
csv += '=== 账户汇总 ===\n';
csv += '账户名称,类型,持仓数量,总市值\n';
state.accounts.forEach((acc) => {
const positions = state.positions.filter((p) => p.accountId === acc.id);
const totalValue = positions.reduce((sum, p) => {
const v = p.currentValue || (+p.amount || 0) * (+p.currentPrice || 0);
return sum + v;
}, 0);
csv += `"${acc.name}","${TYPE_LABEL[acc.type] || acc.type}",${positions.length},${totalValue.toFixed(2)}\n`;
});
csv += '\n=== 持仓明细 ===\n';
csv += '账户,资产,数量,均价,现价,市值,备注\n';
state.positions.forEach((p) => {
const acc = state.accounts.find((a) => a.id === p.accountId);
const value = p.currentValue || (+p.amount || 0) * (+p.currentPrice || 0);
csv += `"${acc?.name || ''}","${p.asset}",${p.amount},${p.avgCost || 0},${p.currentPrice || 0},${value.toFixed(2)},"${p.note || ''}"\n`;
});
csv += '\n=== 交易记录 ===\n';
csv += '日期,账户,资产,方向,数量,价格,手续费,盈亏,备注\n';
state.trades.forEach((t) => {
const acc = state.accounts.find((a) => a.id === t.accountId);
csv += `"${t.date}","${acc?.name || ''}","${t.asset}","${t.side}",${t.amount},${t.price},${t.fee || 0},${t.pnl || ''},"${t.note || ''}"\n`;
});
const finalOutput = format === 'xlsx' ? output.replace('.xlsx', '.csv') : output;
writeFileSync(finalOutput, '\ufeff' + csv, 'utf8');
success(`报告已导出到: ${finalOutput}`);
```
### Technical Analysis
User-controlled values are inserted into CSV cells without escaping embedded double quotes and without neutralizing spreadsheet formula prefixes. Spreadsheet applications may interpret a cell beginning with `=`, `+`, `-`, or `@` as a formula even when the value is quoted in CSV.
Potentially attacker-controlled fields include account names, assets, dates, and notes. These values can originate from manual input, imported files, browser state, or cloud
...[truncated 1181 chars]
- Remediation
- <![CDATA[
## Remediation Suggestions
1. Implement a single CSV cell encoder that:
- Converts values to strings.
- Escapes every double quote as `""`.
- Wraps the resulting value in double quotes.
- Neutralizes cells beginning with `=`, `+`, `-`, `@`, tab, carriage return, or line feed.
```js
function safeCsvCell(value) {
let text = String(value ?? '');
if (/^[=+\-@\t\r\n]/.test(text)) {
text = `'${text}`;
}
return `"${text.replace(/"/g, '""')}"`;
}
```
2. Apply the encoder to every exported string field, including account names, assets, dates, types, and notes.
3. Validate numeric fields and export only finite numeric values.
4. Use a maintained XLSX generation library for actual XLSX output rather than writing CSV under an XLSX option.
5. Add regression tests for formula prefixes, embedded quotes, commas, and multiline values.
6. Warn users when exporting data that originated from untrusted cloud or imported sources.
]]>