blob: cf228dea5d801fe0f214beffccaefd43a571e268 [file] [view]
# Security model verification
Evidence for issues #153–#158. These are automated regression results and source references,
not a claim of human maintainer approval. The security model remains the policy authority.
Tests characterize the stated configuration; neither source inspection nor a passing probe
establishes an OS sandbox. Re-run the named tests when their implementation or dependencies change.
## V1: source reading
`kit/camel-kit-graph/.../ParserSourceReadingContractTest` invokes all nine parsers with
nonempty graph assertions. Fixtures include Java/Groovy code and AST transform canaries,
YAML object tags, external XML entities, runtime-looking placeholders and a secret sentinel.
JFR observes process starts and project-class loads; a loopback HTTP server observes external
entity attempts. The positive control deliberately performs each observed side effect.
The 14 cases passed directly on Java 17, 21 and 25. Isolated mutations moving Groovy compilation
to semantic analysis, substituting a runtime property, loading a project class, starting a
process and enabling XML entity expansion all caused failures. The XML mutation uses element
text: an external entity in an attribute would be rejected even by an otherwise unsafe parser.
These probes protect the covered execution paths; they do not prove safety of arbitrary future
parser/dependency changes. Website impact: not required; this pins existing behavior.
## V2: graph inputs
`GraphInputBoundaryTest` exercises actual parsing and `GraphBuilder`: outside file links,
directory links, an operator-selected root alias, direct supplied outside/oversized lists,
16 MiB and 16 MiB+1 sources, aggregate overflow, large unrelated artifacts and a deliberately
slow POM parser. Production bounds are 16 MiB per input, 256 MiB aggregate recognized source
and 100,000 visited entries. VCS/build/dependency subtrees (`.git`, `.svn`, `.hg`, `target`,
`build`, `node_modules`) are excluded before traversal. Regressions cover a 256 MiB accepted
source set, overflow, copied-resource exclusion, and direct unsafe XML-sniff candidates.
Sources are checked before parser sniffing and bounded again
when read. The POM pass uses the same timeout policy as the remaining parsers.
The baseline reproduced file-link escape, missing POM timeout and oversized-input acceptance.
A timed-out parser's fragment is discarded; daemon threads do not hold the CLI open.
Cancellation remains cooperative, and parser/library expansion can exceed the input byte size:
there is no hard heap quota. Filesystem checks cover a pre-existing hostile tree, not concurrent
same-account directory substitution. Website impact: required; graph reference updated.
## V3: Claude permissions
`ClaudeGenerator` renders MCP grants from the workflow manifest; `ClaudeGeneratorTest` checks
fresh and repeated generation against that manifest. It requires exactly the 46 current MCP grants, with no
server-wide wildcard or duplicate. Existing Read and Maven command grants are unchanged.
Actual Claude Code 2.1.316 was run with generated settings, isolated configuration, a benign
stdio MCP fixture and a loopback model API fixture. For each of camel, camel-knowledge and citrus:
| Rule/tool | Observed behavior |
| --- | --- |
| Listed tool, exact grant | Dispatched without a permission request |
| Unlisted tool, exact grants | Host permission request before dispatch; denied; zero MCP calls |
| Same unlisted tool, former wildcard restored | Dispatched without a permission request |
| Listed tool, its exact grant removed | Host permission request; denied; zero MCP calls |
All 12 cases passed. The fixture drives the actual CLI permission protocol without a remote
model account; it does not certify future Claude releases or unrelated user-level grants.
The reproducible harness is `tools/verify_claude_mcp_permissions.py`; use `--help` for paths.
Website impact: required; agent reference updated. Existing workspaces need regeneration.
## V5: generated and extracted paths
`InitWriteConfinementTest` covers shared directory ancestors, generated file leaves,
all eight agents' nested skill outputs and the positive selected-root-alias case.
`ForageCatalogConfinementTest` covers linked cache ancestors, relative/absolute version escapes
and the exact two-entry archive allowlist. `CitrusSchemaDownloaderTest` covers traversal,
cache/schema links and both generated quick-reference leaves, with an in-memory HTTP archive.
`IndexArchiveConfinementTest` covers traversal, absolute entries and Unix ZIP symlink metadata
through actual hash-verified acquisition. Existing downloader tests cover unsafe activation,
staging, version names and fallback.
Baseline probes demonstrated outside writes for init/Citrus/Forage, including deletion of an
outside directory selected by a malicious Forage version. All outside paths were test fixtures.
The fix rejects unsafe Catalog/Citrus/Forage versions and links in generated paths before
writes/deletions. Positive init fixtures retain links in worktrees, custom skills, agent files,
Maven wrapper siblings, data, schemas, flow docs and Ship state. Managed skill links still fail.
Generated file leaves are replaced through AtomicFileWriter rather than truncated in place;
six hard-link regressions prove outside sentinels remain unchanged for init and Citrus outputs.
Fresh atomic writes use umask-default modes; replacement modes are preserved.
An explicitly chosen root alias is allowed. Extraction creates ordinary files and never
materializes ZIP symlink metadata. These checks do not provide descriptor-relative isolation
against concurrent same-account directory substitution; existing same-account exclusions apply.
Website impact: required; initialization behavior documented in the agent reference.
## V6: Knowledge
`KnowledgeInputBoundaryTest` supplies synthetic Lucene documents to actual service/tool methods.
It checks escaped free text, structured identifiers, upper/default/lower result bounds across all
call sites and catalog-only HTTP/XSLT/exec/file endpoint validation. A conventional HIGH severity
query exposed duplicate elements in `Set.of`; distinct case variants now accept HIGH/high/High.
`LuceneFallbackBoundaryTest` checks pinned failures, readable unpinned temporary fallback,
POSIX temporary-directory privacy where supported, cleanup, persistent-index preservation and
failure when no bundled index exists. Existing downloader tests cover cache permissions, hash,
transport/redirect, pin, staging and activation boundaries. Removing query escaping, increasing
the cap or disabling fallback cleanup each made the respective regression fail.
`OfflineModelsTest` forks fresh JVMs with isolated caches and real bundled embedding/reranker
resources, in both initialization orders. Both return valid inference results with zero observed
HTTP opens, JFR process starts or socket I/O. Four conflicting-environment cases fail before
native/model extraction. All six cases passed directly on Java 17, 21 and 25. Normal cases start
with deliberately conflicting system properties to check enforcement. Removing either provider's
offline call produced four blocked EC2 metadata requests. No real metadata request was sent.
An earlier probe caught JNA launching `ldconfig`; the shared initializer now suppresses implicit
platform-library discovery while preserving an explicitly configured search path.
The offline policy is process-wide DJL configuration for the bundled CPU runtime, not an OS
sandbox. DJL cache and ONNX/native temporary writes remain expected. Conflicting environment
values are rejected rather than silently enabling telemetry or GPU-native downloads.
Packaged transport verification uses `tools/verify_knowledge_runtime.py` with a local synthetic
index: stdio initialize/list-tools/first query, and explicit loopback SSE initialize/list-tools/
query without authorization. It inspects the child process's sockets and syscall trace where
available; JVM local-interface discovery is reported separately from application network.
Website impact: required; Knowledge runtime/reference updated. Published Knowledge 0.0.2 is not
retroactively changed by these source fixes.
### Packaged V6 observation (2026-11-09)
The checked-in probe passed on Temurin 21.0.8+9 with runner SHA-256
`d5e8e7b1a379e75bea6f432d0538d1bc54a75a1143bbb040730f7edb6575e6a6`, with no
baseline/offline/CUDA/JNA command overrides. Both fresh JVMs initialized MCP, listed seven
tools and returned the local fixture from the first query with reranking. Default stdio had
no IP sockets, binds or listeners; explicit SSE had one 127.0.0.1 listener and accepted the
unauthenticated initialization/query. Lifetime syscall traces contained only the initial JVM
exec, no child executable and no application service connection/send. JVM netlink and UDP
port-zero interface discovery was recorded separately. The packaged runner contained all five
model/tokenizer resources and no `knowledge-index/` payload.
## V4: process inventory
Every §4.5 row was checked against the following sources; this is a source audit, supplemented
by the named runtime tests above, rather than a human maintainer attestation:
| Row | Sources and verification scope |
| --- | --- |
| CLI | PrerequisiteChecker/ProcessRunner actual argument vectors and inherited environment; DoctorService wrapper existence check versus mvn execution; OpenCodeGenerator global/ancestor/environment configuration; InitService and Doc/NextId command destinations; CatalogDownloader/CitrusSchemaDownloader/ForageCatalogService origins and request settings. External probes retain their own effects. |
| Graph | GraphBuilder/GraphParser concurrency, deterministic merge and bounds; GraphGenerateCommand/GraphVisualizeCommand JSON/HTML outputs; V1/V2 executable tests. |
| Ship controller | LocalCommandRunner, PiWorker, EvidenceRunner, ShipMavenResolver, ShipNativeWorker, ShipPublicationService: launch vectors, repository and state writes, native handoff, deadlines and publication. Existing controller regression suite remains required. |
| Pi worker | PiWorker.runRpc arguments, environment and working directory; no OS confinement. External Pi/provider implementation is not asserted to be silent or isolated. |
| Evidence JVM | EvidenceRunner/ShipJvmPayloadBootstrap and Camel validation: private environment/temp, classpath and stubbing policy. No claim of an OS socket/process sandbox. |
| Knowledge server | IndexDownloader, IndexResolverConfig, LuceneSearchService, KnowledgeMcpServer, application.properties and shared OfflineTokenizer; V6 tests and packaged-process probe. |
| Indexer | IndexerMain, ApacheCamelDomain, VersionResolver, JiraFetcher, CveParser: initial camel-website main discovery, selected product refs, source/cache roots including jira-cache, index.output and its sibling index.json, client-specific limits; shared embedding/native effects. No blanket filesystem/network quota. |
Dependency audit used resolved DJL 0.36.0, ONNX Runtime 1.19.0 and JNA 5.8.0 source/binary behavior:
DJL InputStream tokenizer factories call Ec2Utils.callHome; generic platform detection consults
CUDA before applying flavor, and JNA can invoke ldconfig unless its platform search path is set.
V6 fixes these observed effects and preserves expected cache/native extraction writes.
The enforced DJL properties and default empty JNA platform path are JVM-wide; explicit JNA
platform paths are preserved. This is a standalone-process policy, not per-tokenizer isolation. ONNX
embedding external-data files and reranker in-memory model loading are described separately.
Negative inventory cells describe inspected application operations and the recorded runtime
configuration. They do not waive P1–P14 or promise behavior for arbitrary external tools,
JVM startup agents or operator-selected native libraries. Website impact: not required for this
source inventory correction; behavior-changing fixes include Antora updates in V2/V3/V5/V6.
Publication of the accepted security model remains tracked by #131.
## Review follow-up (2026-11-10)
The compatibility follow-up narrows init preflight, preserves umask defaults for new generated
files, renders Claude MCP grants from the workflow manifest, and prunes graph build/VCS/dependency
trees. The aggregate graph budget is 256 MiB; the 16 MiB per-source and 60-second parser bounds
remain. Filename filtering precedes validation, and XML content sniffing follows it.
The Python host probes remain explicit evidence reproducers, not CI jobs; Java regressions run
in the normal reactor. Earlier dated host observations describe their original artifacts.