Version: v0
This document describes the implicit security contract between Apache StreamPipes and its downstream operators: what StreamPipes assumes about its environment, what it upholds, what it leaves to the operator, and which "syntactically possible" misuses fall outside the intended design. It serves integrators/operators (which threats they own) and triagers (classifying a scanner/AI/CVE-style finding as valid, out of model, or disclaimed by design — cite the relevant section).
Apache StreamPipes is an open-source Industrial IoT data platform for
connecting industrial data sources, building real-time streaming pipelines, and
exploring time-series data. In-scope component families, based on architecture
boundaries in the root AGENTS.md, are:
| Component | Role | Primary surface |
|---|---|---|
streampipes-rest |
The primary HTTP/REST control-plane boundary: authentication, user/resource management, pipeline control, adapter control, and data-lake queries. The UI drives configuration through this control plane. It is not the external data-ingress path. | network control boundary |
| Connect source/input adapters (extensions) | The external data-ingress path. Adapters connect to external sources (MQTT, Kafka, REST, OPC-UA, HTTP, files, databases, etc.), ingest raw data, normalize it into events, and publish those events onto the internal bus for downstream processing. | external-data ingestion boundary |
| Pipeline elements (processors/sinks) | Processing and sink logic over event streams carried by the internal bus; deployable as extensions. | in-process / extension-runtime code |
| Internal message bus | Internal event transport carrying adapter-normalized and processor-derived events between processing steps. External data enters through source/input adapters; the bus is not directly writable from outside the assumed deployment perimeter. | intra-deployment infrastructure |
| Data lake | Time-series persistence and queryable event history. | intra-deployment infrastructure |
UI (ui/) |
Browser client that drives configuration and visualization through the control plane. | client trust domain |
StreamPipes is not a single hardened process. It is a multi-service deployment that depends on an internal message broker, a time-series/metadata store, extension-runtime services, and the external data sources its adapters connect to. Broker and datastore components are modeled as trusted dependencies inside an operator-controlled, network-isolated perimeter.
| Asset | Why it matters | Primary owner |
|---|---|---|
| Admin accounts and privileged roles | Control over users, adapters, pipelines, and deployment-level configuration. | StreamPipes + operator |
| User accounts, sessions, and access tokens | Access to control-plane APIs and permitted resources. | StreamPipes |
| Initial admin password and client secret | Bootstrap credentials that can become global compromise if left at known defaults. | operator + StreamPipes defaults |
| Adapter/source credentials | Access to industrial systems, databases, message brokers, and external APIs. | operator, protected by StreamPipes handling |
| Pipeline definitions and adapter configurations | Determine what data is ingested, transformed, stored, and sent to sinks. | StreamPipes + operator |
| Event and time-series data | May contain sensitive industrial telemetry or operational information. | operator, protected by StreamPipes access control |
| Internal broker topics/messages | Internal transport for normalized and derived events. | operator infrastructure |
| Extension artifacts and custom code | Execute in extension-runtime containers and may reach broker/datastore services. | operator |
| Logs and error reports | Useful for debugging but must not expose credentials or sensitive payloads unnecessarily. | StreamPipes + operator |
In scope
- An authenticated-but-limited UI/REST user trying to act outside their
role/permissions, such as reading other users' pipelines, modifying resources
they do not own, escalating privileges, or reaching host-level capabilities.
"Limited" is graduated: StreamPipes has a fine-grained role model
(
ROLE_PIPELINE_USER,ROLE_CONNECT_ADMIN,ROLE_DASHBOARD_USER, etc.), not only an admin/user split. - A network adversary between the browser/clients and the REST layer, or between services, where transport security is not configured.
- A compromised or faulty operator-trusted source feeding malformed, oversized, schema-mismatched, or hostile content into an adapter ingestion path. Sources are operator-provisioned, so this is a hostile-content adversary, not an attacker who can introduce a new source. The robustness StreamPipes-owned code guarantees against such content is defined in §5, §7, and §10.
Out of scope
- An operator with host, container, broker, or datastore access. Anyone
controlling the deployment infrastructure is not an adversary StreamPipes
defends against →
OUT-OF-MODEL: adversary-not-in-scope. - A trusted admin performing an authorized action, such as installing an
extension, registering an adapter, changing configuration, or provisioning a
source. A new path to a privilege already held is
OUT-OF-MODEL: equivalent-harm. - Bugs in infrastructure StreamPipes orchestrates or depends on — the
broker, datastore, JVM, or an upstream OPC-UA/MQTT/Kafka library. Report
upstream unless StreamPipes-owned code reaches the vulnerable path with
attacker-influenceable input →
OUT-OF-MODEL: unsupported-component.
StreamPipes has one primary control-plane boundary and one external data-ingress path. The internal bus sits behind the ingress path and is not directly attacker-writable under the assumed deployment architecture.
flowchart LR
Browser[Browser / UI] -->|REST / control-plane requests| REST[streampipes-rest]
REST --> Core[streampipes-service-core]
REST --> Mgmt[*-management modules]
REST --> Lake[Data lake queries]
External[External industrial source] -->|MQTT / Kafka / REST / OPC-UA / files / DB| Adapter[Source/input adapter]
Adapter -->|normalized events| Bus[Internal message bus]
Bus --> Processor[Processors]
Processor -->|derived events| Bus
Bus --> Sink[Sinks]
Bus --> Lake[Data lake]
-
Client (UI/API) → StreamPipes control plane is the control boundary. Requests are authenticated and authorized on two tiers — a role/privilege gate plus a per-object ACL — before acting. Request bodies, pipeline definitions, adapter definitions, and configuration crossing this boundary are treated as untrusted and authorized per resource.
-
External source → source/input adapter is the external data-ingress path. Adapters connect to external systems, ingest raw data, normalize it into events, and publish those events internally. Source provisioning is operator/admin-only; no in-scope lower-privileged adversary can introduce a source. Data from configured sources remains untrusted content for StreamPipes-owned parsers and transformations, but the semantic trustworthiness of that data remains an operator/source concern.
-
Internal bus + inter-service transport run inside an operator-controlled, network-isolated perimeter. Downstream processors may publish derived events internally, but the bus is not directly writable from outside the assumed deployment perimeter.
-
Authentication of UI/REST access before privileged actions. Authentication is stateless and token-based (
TokenAuthenticationFilter,SessionCreationPolicy.STATELESS); form login and HTTP Basic are disabled; OAuth2 login is optional. JWT authentication requires a subject, expiration, an active principal, and an explicitly supported signing algorithm. Configured RSA key errors do not fall back to HMAC. The default rule isanyRequest().authenticated(). The only unauthenticated endpoints are a narrow, non-sensitive asset allowlist for pipeline-element/adapter assets; no control or data endpoint is intended to be unauthenticated by default. -
Authorization is two-tier and per-resource: a role/privilege gate (
@PreAuthorize) plus per-object ACL checks (SpPermissionEvaluator,@PostFilterfor list results). Non-admins are bounded to owned/permitted resources; admins bypass the object ACL by design (§3 item 5). Precedence is: anonymous-if-permitted → admin → per-object ACL. -
Parser and transformation safety for built-in ingestion paths. Malformed syntax or schema-mismatched input in built-in parsers is expected to fail the affected event or adapter operation without compromising unrelated StreamPipes resources. StreamPipes-owned parsing, schema-based transformation, datatype/timestamp coercion, and event handling must not cause code execution, secret disclosure, authorization bypass, corruption of unrelated resources, or uncontrolled cross-adapter impact when reached by hostile external content. This does not guarantee semantic correctness of source data, bounded cost for every oversized payload shape, or robustness of custom extension parsers. Resource bounding is covered separately in §7 and §10.
-
Memory safety on well-formed input to the JVM's extent. StreamPipes is primarily Java; this is not a guarantee against all denial-of-service or resource-exhaustion cases.
- Transport security (TLS) on the REST endpoint, broker, datastore, and inter-service transport, plus the security of those backing services. StreamPipes makes no blanket wire-encryption guarantee by itself (§8).
- Network isolation of the broker, datastore, and extension-runtime services from untrusted networks (§3 item 4, §4).
- Vetting installed adapters and pipeline elements. Installing a JAR extension is deploying code into the runtime — code execution by design, not a sandbox against the extension author (§7).
- Securing external data sources and credentials configured for them. The ingestion path has StreamPipes-owned parser/transformation safety expectations (§5), but the trustworthiness, units, sensor correctness, and business meaning of source data remain operator concerns (§7).
- Setting initial-admin credentials and provisioning the shared service secret and hardening the deployment beyond convenience defaults (§8). Reports against exposed shipped defaults are handled according to §9, not automatically dismissed.
-
A sandbox around installed JAR extensions. Adapters/processors/sinks are deployed as separate container services (for example,
connect-adapters), which provides process/namespace isolation from the core and defense-in-depth against a limited REST/UI user who cannot supply container code. This is not a boundary against whoever builds and installs the extension: that code runs inside the container with its privileges. The shipped compose applies nocap_drop/security_opt/non-rootuser/read_onlyhardening and leaves the broker/datastore reachable on the sharedspnet. Installing an extension is trusting that code.BY-DESIGN: property-disclaimed. Container hardening beyond the default is an operator concern (§8). -
Note — user-supplied JS transform scripts are sandboxed. Unlike installed JARs, the GraalJS transformer (
GraalJsScriptEngine) runs user scripts underSandboxPolicy.CONSTRAINED+HostAccess.CONSTRAINED, withallowHostClassLookupdisabled and host access limited to@ExposedToScriptsmembers. This is a containment boundary: arbitrary host-code execution from a script is not intended, and a break of this sandbox is in-model and VALID. -
Protection against an operator who controls the deployment infrastructure (§3 item 4).
-
Semantic trust in external source data. StreamPipes does not guarantee that data from an external industrial source is truthful, physically plausible, correctly timestamped, correctly typed, or safe for downstream operational decisions. Source trust, units, sensor correctness, and business meaning are operator concerns. This is distinct from parser/transformation safety (§5): hostile content must not compromise StreamPipes-owned code, even though its meaning is not vouched for.
-
Absolute resource fairness across all inputs and pipeline logic. A byte-based, memory-coupled rate limiter (
SpRateLimiter, default-on at extension-service startup) throttles sustained ingress: each event costs permits equal to its byte size, the permit budget is a percentage of JVM total memory, and overload applies backpressure (tryAcquirewith timeout). This is intended to bound sustained high-rate/high-volume sources after metering. It does not guarantee bounded processing for every single malformed or oversized input, nor does it bound compute complexity inside an individual pipeline element. For StreamPipes-owned built-in paths, single-event parsing or processing exhaustion may be in-model (§10). For custom elements, pathological compute or memory behavior is unvetted extension logic and an operator concern.
| Lever | Real name / default | Why it matters |
|---|---|---|
| Initial-admin / auth setup | SP_INITIAL_ADMIN_EMAIL (default admin@streampipes.apache.org), SP_INITIAL_ADMIN_PASSWORD (application default admin; generated by the Compose installer), SP_INITIAL_SERVICE_USER_SECRET (required; no default). The Compose configure helper generates a persistent random service secret and shares it with extensions through SP_CLIENT_SECRET. On first start, AutoInstallation validates the service secret before provisioning accounts. Existing bootstrap accounts using the former public default are migrated when a valid replacement is configured; authentication with the old default is always rejected. |
Service secrets must be provisioned consistently to the backend and extensions and preserved across restarts. The Compose installer also generates the initial human-admin password, CouchDB password, InfluxDB token and admin password, and encryption passcode. Deployments using other tooling must supply their own credentials. |
| Role / permission model | Two-tier: @PreAuthorize privilege + per-object ACL (SpPermissionEvaluator). Fine-grained roles (ROLE_CONNECT_ADMIN, ROLE_PIPELINE_USER, etc.). |
Bounds non-admins to owned/permitted resources and determines which roles may configure adapters/pipelines. |
| Auth on endpoints | anyRequest().authenticated(); unauthenticated allowlist = pipeline-element/adapter asset paths only. |
No control/data endpoint is intended to be unauthenticated by default. |
| Rate limiter | SpRateLimiter, default-on; permit budget = percentage of JVM memory, plus warmup and acquire-timeout env vars. |
Bounds sustained ingress rate/volume after metering and applies backpressure. It is not a complete guarantee against single-event parsing exhaustion. |
| Extension container hardening | Shipped compose: no cap_drop/security_opt/non-root user/read_only; extension services on shared spnet. |
Container isolation exists, but default hardening is minimal. |
| TLS on REST + broker + datastore | Operator-configured. | Determines whether sessions/events are protected on the wire. |
-
"A custom adapter / processor JAR can run arbitrary code." By design — installing a JAR extension is an authorized code deployment, not a sandbox escape.
BY-DESIGN. -
"The JS transformer runs user script, therefore RCE." Not by itself. The GraalJS engine runs scripts under a constrained sandbox with host-class lookup disabled and host access limited to
@ExposedToScripts. In-model and VALID only if the report demonstrates breaking that sandbox, escaping the constrained policy, or abusing an exposed host surface in a way that violates the intended containment boundary. -
"An adapter can be pointed at an internal URL (SSRF)." Configuring an adapter target requires the
WRITE_ADAPTERprivilege, held byROLE_CONNECT_ADMIN, not a plain user. A Connect Admin choosing where to connect is an authorized action (§3 item 5) →OUT-OF-MODEL: equivalent-harm. In-model and VALID only if a lower-privileged role can set the target, or if an authorization bypass lets a user modify another user's adapter configuration. -
"CSRF protection is disabled." Not automatically a finding for the default stateless token-auth model. A CSRF report is in-model only if StreamPipes itself stores or sends authentication material in a browser-ambient way, such as a cookie-bearing session, or if a shipped default creates such a condition. An operator-created cookie/session wrapper is
OUT-OF-MODEL: non-default-build. -
"An unauthenticated endpoint is reachable." In-model only if it is outside the shipped non-sensitive asset allowlist (
/sec|sepa|stream/*/assets/**,/api/v1/worker/adapters/*/assets/**) or if the endpoint exposes control, data, credentials, secrets, or sensitive metadata. An operator-loosened auth config isOUT-OF-MODEL: non-default-build. -
"The instance is reachable with admin/admin or the default client secret." A production-reachable fresh installation that accepts shipped bootstrap credentials is security-relevant and should not be automatically routed to
OUT-OF-MODEL: non-default-build. It may be treated as operator misconfiguration only when production installation clearly requires overriding those values and StreamPipes prominently warns, blocks startup, generates instance-unique values, or forces rotation before use. If those safeguards are absent or weak, triage as a hardening gap or valid shipped-default issue. -
"A high-rate source OOM-crashes the extension service." A report based only on sustained high event rate is not sufficient if the default limiter properly applies backpressure. A report is in-model if it shows that the limiter is bypassed, disabled by default, ineffective on a built-in path, or unable to prevent single-event parsing exhaustion.
-
"A pathological pipeline element hangs or uses super-linear CPU." The rate limiter bounds sustained ingress bytes after metering, not arbitrary per-element compute. For a built-in element, this may be a bug. For a custom element, it is unvetted extension logic and an operator concern (§7).
-
Dependency-tail CVEs from an SCA scanner, such as in a Kafka/MQTT/OPC-UA client or transitive JAR, are triaged upstream unless StreamPipes-owned code reaches the vulnerable path with attacker-influenceable input. A raw CVE-in-a-JAR report with no reachable path is not by itself a StreamPipes finding.
Route every finding to exactly one disposition below and cite the justifying section named in parentheses.
A finding is VALID only when all of the following hold: the violated property is one StreamPipes claims (§5), the attacker is in scope (§3), and the affected code is on an in-model surface (§2/§4) reached by untrusted input.
Otherwise route to exactly one of:
OUT-OF-MODEL: adversary-not-in-scope— the attacker is excluded by §3.OUT-OF-MODEL: equivalent-harm— the attacker already holds equivalent capability, so the property is not one StreamPipes upholds (§3 item 5).OUT-OF-MODEL: unsupported-component— the affected surface is not a modeled component (§2); e.g. broker, underlying datastore infrastructure, JVM, or other perimeter-internal infrastructure without a StreamPipes-owned reachable path.OUT-OF-MODEL: non-default-build— the issue depends on an operator-created or non-default configuration outside the shipped security model; e.g. operator-disabled TLS, loosened auth rules, an operator-created cookie session, exposed broker/datastore ports, or an unvetted installed extension.BY-DESIGN: property-disclaimed— the property is one StreamPipes explicitly does not uphold (§7); e.g. installed JAR extension code execution.
Do not classify shipped-default bootstrap credentials as non-default-build
merely because the operator failed to override them. Use §9's default-credential
triage note.
VALID examples
- A malformed JSON, XML, CSV, Avro, or timestamp payload crashes the adapter service or causes an uncontrolled restart loop in StreamPipes-owned code.
- A hostile timestamp string causes the adapter to stop emitting all events instead of failing only the affected event or adapter operation.
- A single oversized document exhausts memory/CPU during parsing, before the rate limiter meters it, in StreamPipes-owned code.
- A malformed adapter payload causes credentials, tokens, connection strings, or secrets to be logged or returned via API.
- A non-admin user can modify schema mappings, timestamp conversion, adapter targets, or adapter configuration outside their permissions.
- Hostile input reaches a StreamPipes-owned parser or transformation path and triggers code execution, deserialization abuse, unauthorized file/network access, or corruption of unrelated resources.
- A built-in pipeline element has super-linear behavior reachable from hostile source content and can be used to exhaust CPU or memory across unrelated resources.
Normally OUT-OF-MODEL / data-quality examples
- A sensor sends a false but well-formed temperature value.
- A source sends a timestamp with the wrong timezone, and StreamPipes processes it according to the configured parser.
- An operator maps the wrong schema attribute to the timestamp field.
- An admin intentionally installs a custom adapter, processor, script, or extension that changes event semantics.
- An operator exposes the broker, datastore, or extension runtime to an untrusted network contrary to deployment assumptions.
- A sustained high-rate source is throttled by
SpRateLimiterrather than crashing the service.
The following seed threats are not an exhaustive vulnerability list. They are starting points for future review, regression tests, and triage.
| ID | Category | Surface | Threat | In-scope when | Primary mitigation / expectation | Status |
|---|---|---|---|---|---|---|
| T1 | Spoofing / Elevation of privilege | REST control plane | User acts as another user or escalates role. | Limited UI/REST user can bypass authn/authz. | Token auth, role gates, per-object ACL. | Modeled |
| T2 | Tampering | Pipeline/adapter resources | User modifies another user's pipeline, adapter, schema mapping, or timestamp conversion. | Per-object ACL or privilege check can be bypassed. | @PreAuthorize, SpPermissionEvaluator, @PostFilter. |
Modeled |
| T3 | Information disclosure | Adapter/source configuration | Source credentials or client secrets are exposed via API, UI, logs, or error messages. | Reached by lower-privileged user or hostile input. | Secret redaction, ACL, safe error handling. | Modeled |
| T4 | Denial of service | Built-in ingestion path | Malformed or oversized input crashes an adapter or causes uncontrolled restart/resource exhaustion. | Occurs in StreamPipes-owned parser/transformation/event-handling code. | Per-event failure, bounded parsing, rate limiting, safe errors. | Modeled |
| T5 | Elevation of privilege | JS transformer | User script escapes GraalJS sandbox into host-code execution. | Script breaks intended constrained sandbox or exposed host surface. | Graal sandbox policy and narrow host exposure. | Modeled |
| T6 | Elevation of privilege / SSRF-shaped authz issue | Adapter target configuration | Lower-privileged user points adapter at internal URL or modifies another user's target. | User lacks WRITE_ADAPTER or target resource permission. |
Role gate and object ACL. | Modeled |
| T7 | Information disclosure / Tampering | Internal bus/datastore | External actor reads/writes broker topics or datastore. | Only if exposed by shipped defaults or StreamPipes config; otherwise operator infra issue. | Network isolation, broker/datastore auth/TLS. | Operator/shared |
| T8 | Spoofing / Elevation of privilege | Bootstrap credentials | Fresh production-reachable instance accepts known shipped defaults. | Defaults usable without forced rotation, generated secret, block, or prominent warning. | Require override, generate unique secret, warn/block/rotate. | Hardening gap / modeled |
| T9 | Denial of service | Built-in pipeline element | Pathological built-in processor causes super-linear CPU/memory on hostile content. | Built-in StreamPipes element reachable by in-scope hostile content. | Complexity bounds, validation, tests. | Modeled |
| T10 | Supply chain / Equivalent harm | Installed JAR extension | Custom extension executes arbitrary code. | Installed by trusted admin/operator. | Operator vetting; not a sandbox boundary. | By design |
This threat model is owned by the Apache StreamPipes PMC. Review it when StreamPipes adds or changes a public-facing surface, adapter class, authentication mechanism, authorization model, extension mechanism, broker or datastore integration, default deployment mode, or bootstrap credential behavior.