Project
Security
What a build reads, writes and never does, with the source file that decides each.
// @generated by loclizr abi=1. Do not edit; run `loclizr build`.Every file the build may delete starts with that line. Each claim names the enforcing file in
packages/loclizr/src.
What a build reads
Section titled “What a build reads”| Input | Where | How |
|---|---|---|
| Catalogs | the catalogs pattern, locales/{locale}.json by default |
Parsed as JSON. Never written. |
| Translator notes | meta, locales/en.meta.json by default |
Parsed as JSON. |
| The config | loclizr.config.ts or --config |
Executed in-process by jiti (src/config/index.ts), imports included. Review it like a vite.config.ts. |
| Your sources | scan.include, src/**/*.{ts,tsx,js,jsx,mts,mjs,svelte,vue,astro} by default |
Scanned for usage (src/scan/index.ts). Never written or executed. Symbolic links are followed; a target that is not a regular file is skipped unread. |
| The existing tree and record | outDir and record |
Compared byte for byte: unchanged files are skipped, check reports staleness. |
What it writes and deletes
Section titled “What it writes and deletes”| Command | Writes |
|---|---|
build |
Files carrying the header under outDir, a .gitignore there only when the build creates outDir, and the record (locales/loclizr.context.json by default), never over a file it cannot tell is a record (LZ5001). Each lands in a temporary file, then is renamed into place (src/compiler/output.ts). |
init |
loclizr.config.ts and locales/en.json with the wx flag, never overwriting (src/cli/init.ts). |
check |
Nothing. |
The prune deletes only files carrying the header that this emit did not produce. A headerless file
stays, reported as LZ1021. Symbolic links under outDir are never read, written or deleted.
What it never does
Section titled “What it never does”- No network. The built entries import only
node:async_hooks,node:crypto,node:fs/promises,node:pathandnode:util; none of the seven production packages imports a network or child-process module. - No telemetry, update check or child process.
Environment variables read:
| Reader | Variables |
|---|---|
| loclizr | NODE_ENV (drops development warnings), NO_COLOR |
jiti |
JITI_*, TMPDIR; through its bundled Babel, NODE_ENV, DEBUG, NODE_DEBUG, BABEL_* |
picocolors |
FORCE_COLOR, NO_COLOR, TERM, CI |
tinyglobby |
TINYGLOBBY_DEBUG |
Dependency surface
Section titled “Dependency surface”| Entry | Imports |
|---|---|
loclizr (what an app ships) |
nothing outside its own files |
loclizr/react |
react, an optional peer |
loclizr/server |
node:async_hooks |
Production dependencies serve only the compiler and CLI, never the browser:
| Direct | Pulls in | Used for |
|---|---|---|
@formatjs/icu-messageformat-parser |
@formatjs/icu-skeleton-parser |
Parsing ICU at build time |
jiti |
Loading a TypeScript config | |
picocolors |
Colour in the human reporter | |
tinyglobby |
fdir, picomatch |
Expanding scan.include |
typescript is a development dependency only: declarations are printed as text.
Catalog text cannot become code
Section titled “Catalog text cannot become code”Catalog text lands in string literals, never expressions (src/emit/shared.ts):
templateTextescapes backslash, backtick, every$(a trailing$beside an argument would form${), carriage return, newline and the two Unicode line separators.- Keys and option values go through
quoted. .d.tsdoc comments escape*/and fold every line break, the two Unicode line separators included, to a space, so a source string can neither close one early nor start a line that TypeScript reads as a directive or JSDoc tag.
Direction controls (U+202A to U+202E, U+2066 to U+2069) stay in the text as written. One left
open would reverse the page text after the message, so an unpaired control in any value is
LZ3014 bidi-control-unpaired at warn (src/check/index.ts).
Path containment
Section titled “Path containment”outDir must resolve inside the project root, checked twice:
| When | Check | Failure |
|---|---|---|
| Config load | Lexical (src/config/resolve.ts) |
LZ1007, exit 2 |
| Write time | realpath (src/compiler/output.ts) |
LZ5001; nothing is written or pruned under it |
The second check catches an outDir linking outside the project, or a link below it such as
outDir/messages. The prune never follows a link: the header cannot prove the build wrote its target.
Hostile headers and cookies
Section titled “Hostile headers and cookies”localeFromHeaders and negotiate (src/server/index.ts) pass the cookie and every
Accept-Language range through matchLocale (src/runtime/store.ts), which returns a declared
tag or the source locale. Only that reaches Content-Language and the request scope. The client
uses the same matcher and reads <html lang> only when tag-shaped.
Terminal escapes in diagnostics
Section titled “Terminal escapes in diagnostics”Keys, source text, descriptions and paths print through one sanitizer (src/diagnostics/index.ts).
C0 controls except tab and newline, DEL and C1 print as <U+XX>: no erased output, no planted
OSC 8 links in CI logs. The JSON reporter escapes DEL and C1 as \uXXXX, losslessly.
Not covered
Section titled “Not covered”- CSP. No guidance; generated code is plain modules with no inline script.
- The locale cookie.
path=/,SameSite=Lax, one yearmax-age, noSecure, noDomain; only its name is configurable. It cannot beHttpOnly:setLocalewrites it from the browser.
Reporting
Section titled “Reporting”No SECURITY.md yet, so no private reporting channel; this page will link it.