Please report vulnerabilities privately through GitHub Security Advisories: https://github.com/afkzonagroup/plotline/security/advisories/new. Do not open a public issue. If you cannot use GitHub, email info@afkzonagroup.lt.
Include the version, what you did, what happened, and a minimal example without real farm locations or producer names. We aim to acknowledge reports within five working days and to agree a disclosure date with you.
Supported versions: the latest release.
- Geolocation data: plot boundaries and points, often with producer names. For smallholders this identifies a person's land: personal data.
- Validation results: operators rely on them before submitting a due diligence statement. A wrong "OK", or a repair that silently changes a plot, can lead to a rejected or incorrect statement.
- The user's machine: files the CLI reads and writes, the terminal, the CI runner that runs the GitHub Action.
- The operator's analyst who runs Plotline (trusted, may make mistakes).
- A hostile or careless data source: suppliers, cooperatives and third-party tools produce the files. Assume any file can be crafted.
- A hostile contributor to a repository that the GitHub Action checks: can add files and choose their names and content.
- File content entering any reader (GeoJSON, KML, CSV), in every interface.
- File names and paths given on the command line or found by
--glob. - Output to the terminal and to GitHub workflow commands.
- Files written by the CLI.
- The offline page: a file dropped into a browser page.
| Threat | Mitigation | Test |
|---|---|---|
| Memory or CPU exhaustion by large, deep or numerous values | Byte limit checked before reading and decoding; iterative JSON and XML parsers (no recursion) with depth, value-count, string-length and number-length limits; feature and position limits; every limit has a hard maximum | test/json.test.ts, test/hostile.test.ts, test/readers.test.ts |
| Quadratic geometry checks (self-intersection, overlaps) on adversarial shapes | Sweep-line checks charged to a work budget; when it runs out, Plotline stops and reports check-limit-reached instead of hanging |
test/geometry.test.ts, test/hostile.test.ts |
| XML entity expansion (billion laughs) and external entities (XXE) | Any <!DOCTYPE or other <! declaration is refused; only the five predefined entities and numeric references are decoded; no external resource is ever loaded |
test/readers.test.ts |
Prototype pollution through keys such as __proto__ |
Parsed objects are Maps; keys are only data; summaries use null-prototype objects |
test/json.test.ts, test/hostile.test.ts |
| Ambiguous JSON (duplicate keys, unpaired surrogates, NaN, Infinity, numbers beyond double range) | Rejected as errors (RFC 8259 grammar, I-JSON) | test/json.test.ts |
| Invalid UTF-8 | Strict decoder; the file is refused | test/readers.test.ts |
| Silent data change by repairs | Repairs limited to changes that keep location and area; every change listed; idempotence tested; files in another coordinate system are never rewritten; unreadable features kept verbatim | test/fix.test.ts |
| Floating-point errors in geometry decisions | Integer micro-degree coordinates and exact orientation predicates (BigInt fallback in TypeScript, Python integers) | test/geometry.test.ts |
| Terminal escape injection through file names or property values | Control, C1, bidirectional and line-separator characters are escaped in all text output | test/cli.test.ts |
| GitHub workflow command injection through file names or messages | %, CR, LF (and : , in properties) are escaped as GitHub documents; annotations are built only from the current run's in-memory report; the action's failed output is derived from the exit code only |
test/cli.test.ts, test/action.test.ts |
| Path tricks on output (overwrite input, follow links, clobber files) | Output may not be the input; files created with O_EXCL unless --force; O_NOFOLLOW on the target; mode 0600 files, 0700 directories; split part names are generated, never taken from input |
test/node-io.test.ts, test/cli.test.ts |
| Reading devices or FIFOs | Only regular files are read; opened non-blocking | test/node-io.test.ts |
--glob leaving the root or hanging |
Relative patterns only, no ..; symbolic links are not followed; .git and node_modules skipped; matching is polynomial (no regular expressions built from the pattern); file-count limit |
test/node-io.test.ts |
| Stack traces or internals leaking | Unexpected errors produce a generic message | test/cli.test.ts |
| Offline page sending data out | Content Security Policy default-src 'none', connect-src 'none', form-action 'none', script and style by SHA-256 hash only, require-trusted-types-for 'script' with no policies; links allow-listed to fixed official pages without query strings; no storage APIs |
test/app-build.test.ts, test/app-dom.test.ts |
| Script injection in the offline page | The page is built with createElement and textContent only; attribute names are allow-listed; no HTML parsing sinks |
test/app-build.test.ts, test/source-hygiene.test.ts |
| Supply chain | No runtime dependencies in either package; exact pinned development dependencies; lockfile; no install scripts; npm audit in CI; pinned action SHAs; Docker base image pinned by digest, runs as a non-root user |
test/source-hygiene.test.ts |
Plotline reads the files you give it and writes only the files you name (or
the parts and manifest in the directory you name). The library, CLI, Python
package and offline page make no network connections and collect no
telemetry. The Docker image works with --network none. The GitHub Action
runs inside your workflow and sends nothing anywhere; annotations go to your
own repository's run.
- Run the Docker image with
--cap-drop=ALL --security-opt no-new-privileges --network noneand mount only the directory with your files. - In GitHub Actions, give the job
permissions: contents: readand check out withpersist-credentials: false. Pin the action to a full commit SHA.