Skip to content

Security Considerations

A Workfile composes calls to external systems, using credentials that a deployment holds, over data that often arrives from an untrusted source. The following properties are what a deployment can rely on, and what it must still supply.

Untrusted input reaches expressions. A trigger payload is attacker-influenced in many workflows. The format bounds the work that such input can cause:

  • matches, extract, and extract_all MUST use RE2 syntax, with no backreferences and no lookaround, and MUST match in linear time. A backtracking engine would make a pattern evaluated against inbound data a denial-of-service target.
  • for_each requires max, and repeat requires a numeric max. unbounded-iteration is a separate capability, so a deployment can decline unbounded loops.
  • call depth and partial inclusion depth are bounded, so neither can recurse.
  • large-payloads is a capability, so a deployment can bound the size of the data that a run holds.

A Workfile cannot choose a request destination. Connection parameters are not in scope for expressions. A parameter that contributes to the host MUST declare a pattern, and implementations MUST reject a manifest that omits one. A connector whose address an operator chooses declares self_hosted: true, and implementations MUST apply a deployment-configured host policy to it. That policy SHOULD exclude addresses that resolve to private, loopback, or link-local ranges. No value inside a Workfile can therefore redirect a credentialed request.

Credentials stay outside run state. A Workfile names a connection and never reads it. Credentials are not readable from any expression, and implementations MUST NOT record credential material in a run record. A recorded action invocation holds the evaluated arguments and the connection name.

Least privilege is computable. A manifest declares scopes per action and per trigger. The set that a file requires is the baseline plus the scopes of the actions and triggers that it names. An implementation that can read a connection’s granted scopes MUST verify that set before any action executes.

Composed output is escaped by construction. A render template escapes every interpolation according to its context, and there is no opt-out. The text context escapes nothing, so an author who renders untrusted data into markup MUST use an html or json context. For strings composed outside a template, the escaping filters cover HTML, JSON, URL, and shell contexts.

Sandboxed code is bounded but reads the whole scope. A run step cannot invoke an action, cannot schedule work, cannot read the filesystem, and has no network egress by default. Its ctx is the full readable scope, so a run step with a network grant can send any readable binding to a granted host. A deployment that treats run state as sensitive SHOULD decline the run-python and run-javascript capabilities, or SHOULD review every network grant.

Autonomy is bounded and recorded. An agent node reads exactly its with projection and its rendered goal. Implementations MUST refuse an action outside the node’s tools allowlist, MUST validate every invocation against its manifest, and MUST record every invocation. budget is required, so a node cannot run without a stated bound.

A run record holds payload data. Recording is mandatory and complete, so a record accumulates the arguments and the results of every action. A deployment MUST apply its own retention policy and access controls to run records. This standard defines no storage format and no redaction mechanism.

Manifests and packages are trusted input. A validator acts on a manifest’s promises: idempotent gates re-issue, validated_output gates exhaustive dispatch, and an error table decides what is retried. A false promise is not detectable from the file that relies on it. A deployment SHOULD resolve only from catalogs that it trusts, and MUST record the resolution for every run. A filter package executes as code in the expression layer, and implementations MUST verify its fixtures before they load it.

Ambiguous outcomes default to a stop. Where an action’s outcome is unknown and the manifest makes no idempotency promise, the default is halt rather than re-issue. A duplicate side effect requires an explicit promise plus an explicit key.