Skip to content
Closed
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
ea29ee9
Fix CSV formula injection NUL byte bypass
seonghobae Aug 31, 2026
f344b11
Fix CSV formula injection NUL byte bypass
seonghobae Aug 31, 2026
df8b85d
Trigger CI retry
seonghobae Aug 31, 2026
447629a
Trigger CI retry
seonghobae Aug 31, 2026
3bf327f
test(security): cover repeated NUL CSV prefixes
seonghobae Aug 31, 2026
42d8ee3
Trigger CI retry
seonghobae Aug 31, 2026
51c8b28
Trigger CI retry
seonghobae Aug 31, 2026
15a274a
Trigger CI retry
seonghobae Aug 31, 2026
c60de01
Trigger CI retry
seonghobae Aug 31, 2026
4ddc5c6
Trigger CI retry
seonghobae Aug 31, 2026
e732ff6
Trigger CI retry
seonghobae Aug 31, 2026
2195e67
Trigger CI retry
seonghobae Aug 31, 2026
2e56581
Trigger CI retry
seonghobae Aug 31, 2026
2839a6b
Trigger CI retry
seonghobae Aug 31, 2026
f72c4ca
Trigger CI retry
seonghobae Sep 1, 2026
08dbef7
Trigger CI retry
seonghobae Sep 1, 2026
fcb7a8c
test(security): preserve NUL-only CSV regression
seonghobae Sep 1, 2026
d9842bd
docs(security): date NUL CSV lesson to current repair
seonghobae Sep 1, 2026
801e01b
test(security): preserve full-width CSV operator regressions
seonghobae Sep 1, 2026
910636e
fix(security): preserve full-width CSV operator guard
seonghobae Sep 1, 2026
2eb9bb2
chore(stack): refresh CSV security protected base
seonghobae Sep 4, 2026
754fa51
test(security): reject C0-prefixed CSV fields
seonghobae Sep 5, 2026
12043a9
fix(security): reject leading C0 CSV controls
seonghobae Sep 5, 2026
419651f
merge(develop): adopt protected workflow backstops
seonghobae Sep 5, 2026
71c2772
docs(security): record C0 CSV control boundary
seonghobae Sep 5, 2026
b1328c3
Trigger CI retry
seonghobae Sep 5, 2026
5c431fc
Trigger CI retry
seonghobae Sep 5, 2026
694018a
Trigger CI retry
seonghobae Sep 5, 2026
91eace8
Trigger CI retry
seonghobae Sep 6, 2026
2cb0672
test(security): restore CSV C0 and full-width regressions
seonghobae Sep 6, 2026
4ed51b2
fix(security): restore CSV control-prefix boundary
seonghobae Sep 6, 2026
15b90b2
docs(security): restore CSV C0 trust-boundary note
seonghobae Sep 6, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .jules/sentinel.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,8 @@
**Vulnerability:** The Rust backend (`apps/desktop/src-tauri/src/main.rs`) did not enforce a maximum URL length limit when processing YouTube URLs via `import_youtube_url`. While the frontend enforced `MAX_YOUTUBE_URL_LENGTH = 2000` via the input element, this could be bypassed by an attacker sending requests directly to the Tauri backend API, potentially causing a Denial of Service (DoS) due to unbounded URL parsing and regex matching.
**Learning:** Input validation must occur at the entry point of untrusted data on the backend, even if it is also validated on the frontend. Relying solely on frontend validation for constraints like string length can expose the backend to resource exhaustion vulnerabilities.
**Prevention:** Always enforce constraints like maximum length, format validation, and sanitization at the earliest possible point on the backend, typically at the API boundary, regardless of frontend safeguards.

## 2025-02-15 - CSV Formula Injection NUL Byte Bypass
**Vulnerability:** CSV formula injection mitigation was incomplete, missing the NUL byte (`\x00`) in its control character check.
**Learning:** Regular expressions for CSV escaping must explicitly include NUL bytes, as some parsers might skip them and execute the subsequent formula.
**Prevention:** Include `\x00` in the regex for problematic characters (e.g. `/^[\s\uFEFF\xA0]*[=+\-@\t\r\n\x00]/`), and explicitly suppress ESLint `no-control-regex` to allow it.
6 changes: 6 additions & 0 deletions apps/desktop/src/lib/export.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,12 @@ describe("export sanitization", () => {
expect(escapeCsvField("\t+SUM(A1)")).toBe("'\t+SUM(A1)");
expect(escapeCsvField("\n-100")).toBe("\"'\n-100\"");
expect(escapeCsvField("\r@cmd")).toBe("\"'\r@cmd\"");

// Prevent bypasses using NUL bytes
expect(escapeCsvField("\x00=1+2")).toBe("'\x00=1+2");
expect(escapeCsvField(" \x00@cmd")).toBe("' \x00@cmd");
expect(escapeCsvField("\x00\x00=1+2")).toBe("'\x00\x00=1+2");
expect(escapeCsvField(" \x00\x00@cmd")).toBe("' \x00\x00@cmd");
});
Comment thread
devin-ai-integration[bot] marked this conversation as resolved.

it("handles combined scenarios: formula injection with structural characters", () => {
Expand Down
3 changes: 2 additions & 1 deletion apps/desktop/src/lib/export.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ export function sanitizeFilename(title: string): string {
export function escapeCsvField(value: string): string {
let escapedValue = value;
// Prevent CSV formula injection by prefixing problematic leading characters with a single quote
if (/^[\s\uFEFF\xA0]*[=+\-@\t\r\n]/.test(value)) {
// eslint-disable-next-line no-control-regex
if (/^[\s\uFEFF\xA0]*[=+\-@\t\r\n\x00]/.test(value)) {
Comment thread
devin-ai-integration[bot] marked this conversation as resolved.
Outdated
escapedValue = `'${value}`;
}
// Enclose in double quotes if there's a comma, newline, or double quote
Expand Down
Loading