Skip to content

fix(compiler): reject includes that leave the sketch project before arduino-cli runs - #160

Merged
ttbombadil merged 1 commit into
mainfrom
fix/compile-include-guard
Oct 3, 2026
Merged

ttbombadil merged 1 commit into
mainfrom
fix/compile-include-guard

Conversation

@ttbombadil

Copy link
Copy Markdown
Collaborator

Purpose

R1 of the refactoring series (docs/UNOSIM_REFACTORING_OPL.md), audit finding S1.

POST /api/compile runs arduino-cli inside the backend process/container, not in a sandbox. Unresolved #include lines are passed through unchanged and stderr reaches the client unfiltered.

Verification of the finding (synthetic sentinels only)

Sub-finding Result
Absolute #include "<file>" makes the compiler read any readable file and echo it in diagnostics Confirmed – a synthetic sentinel file appeared verbatim in the REST compile result (real arduino-cli)
Env leak via inherited env and /proc/self/environ / /proc/1/environ Falsified – g++ 12 (sandbox image) and avr-g++ 7.3 (backend image) read both as empty via #include and .incbin; a synthetic env variable never appeared. No env allowlist is added.
.incbin embeds regular files into the HEX, which the REST response currently returns Confirmed – closed by R6 (binary leaves the REST payload)
GAS .include in inline asm echoes ~10 leading chars per line in assembler errors; string-literal concatenation defeats textual filtering Confirmed – no small robust fix; recorded as BLOCKED_DECISION (sandboxed REST compile is an architecture decision)

Change

  • server/services/compiler/include-guard.ts: conservative scan of every submitted file (main + headers) after line splicing. A directive candidate is any # / %: / ??= preceded only by whitespace or comments. include, include_next, import, embed and __has_include(_next) must name a literal "file" or <file> that is not absolute, has no backslash, and – for quoted names – stays inside the project relative to the including file; angled names may not contain ... Computed includes and escaped directive names are rejected.
  • ArduinoCompiler.compileInternal rejects such sketches before the cache or arduino-cli is consulted, with a normal compile error (file:line: error: …).
  • Simulation compiles (sandbox g++, local developer mode) are unchanged.

Tests

  • RED → GREEN: tests/server/services/compiler/include-guard.test.ts (25 cases incl. digraph, splices, comments, computed and project-internal ../ includes), tests/server/services/arduino-compiler-include-guard.test.ts (2).
  • tests/integration/compile-include-boundary.test.ts in the integration-toolchain project: with real arduino-cli, the sentinel was in the response before the fix and is absent after it.
  • Survey: UnoSim-Examples, fixtures, public examples and E2E contain no include the guard rejects; existing ../shared/pins.h tests stay green (entry file in src/).
  • npm run check, ESLint, unit 2697 passed, integration-toolchain 15/15, pre-push incl. Sonar quality gate PASSED.

🤖 Generated with Claude Code

…rduino-cli runs

The REST compiler runs arduino-cli in the backend. An absolute or traversing
include made it read any readable file and echo it in diagnostics (verified
with a synthetic sentinel file). The env-leak part of the audit finding was
falsified: /proc/*/environ reads as empty for both compilers in use.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ttbombadil
ttbombadil merged commit 1c2995d into main Oct 3, 2026
5 checks passed
@ttbombadil
ttbombadil deleted the fix/compile-include-guard branch October 3, 2026 20:50
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.

1 participant