You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
finding: the whole /automation read domain is gated only by "authenticated" — run-detail returns the triggering record's fields without that record's own FLS #7900
Observation-class finding, filed unassigned with no pm:queue — for triage to grade and route. Handed up by the dev on #7639 / PR #7896 as an explicit "for you to grade, not for me to decide" residual, and graded by the services PM as not a blocker for that PR (reasoning below) but worth its own card.
What was measured
packages/runtime/src/domains/automation.ts:135-147 applies the #5519anonymous baseline to the whole /automation domain — not per route, not per field. automation.ts:489-496 then answers run-detail with deps.success(run), handing back the ExecutionLogEntryverbatim: no projection, no redaction, no masking, on any field, anywhere on that path. A repo-wide grep for redact/mask/sanitize across service-automation and the domain returns zero non-comment hits.
So the only question this surface asks is "are you authenticated?". Any authenticated caller who knows a run id reads whatever that run's log entry holds — including, via the variables snapshot and via output, the triggering record's fields, without that record's own field-level security applying.
Why it is worth a card
The exposure is not new and was not introduced by #7639/PR #7896, which is exactly why it needs its own card rather than a veto on that PR:
output has always carried record fields whenever a flow author declared the record (or a projection of it) as an output variable — so the FLS-bypass property predates the paused-variables work.
steps, flow definitions and the rest of the /automation read surface sit behind the same single gate.
Framing this as "a problem with the variables key" would be the inaccurate version. The accurate question is a domain-level policy one:
Should the /automation read surface apply the underlying record's FLS (or any per-object permission) rather than only an authentication check?
That is a product/security design call with real cost on both sides — narrowing it changes what every existing operator tool can read, and per-field filtering of a free-form variable map is not obviously well-defined (keys are <nodeId>.<key>, record, previous, $runId, seeded inputs; only some of them correspond to a record field at all).
What the other door does today
The identical snapshot has a second, differently gated door: sys_automation_run.variables_json (sys-automation-run.object.ts:164-169) persists it verbatim for every paused run, and that read goes through the system object's permissions. So the platform currently answers the same question two ways depending on which door you knock on — which is itself the sharpest evidence that a deliberate policy has never been stated for this surface.
Explicitly NOT claimed here
No credential class is exposed. Measured on finding: a suspended flow run's variables are unreadable on run-detail — the paused recordLog omits them while the continuation keeps them #7639: connector credentials live in the connector registry and are never handed to a flow; the http node's signingSecret/headers are config inputs interpolated from variables and never written back (builtin/http-nodes.ts:115-170, output is {response, status}). A secret reaches variables only if a flow author puts one there — and by that same route it reaches output. This card is about record-field FLS, not secret leakage.
Landing site is packages/runtime/src/domains/automation.ts (the gate) and possibly plugin-security (whatever would supply the record's FLS), so this likely is notdomain:services even though it surfaced there — the service-automation engine is not where the gate lives. Triage's call.
If graded for work, this probably wants needs-user-decision before implementation rather than a dispatch: the narrowing decision has a real blast radius, and per #4829 the time to put it in the maintainer's inbox is when there is pull — which there now arguably is, since PR #7896 makes the exposure unconditional.
Evidence
Measured 2026-08-12 by the dev on PR #7896 (origin/main base 68a1edb), reading packages/runtime/src/domains/automation.ts, packages/services/service-automation/src/engine.ts, builtin/http-nodes.ts, and sys-automation-run.object.ts. Full measurement recorded in the <!-- os-dev-report --> comment on #7639 and in PR #7896's body.
Source
Split out of #7639 (PR #7896) — the dev's named residual, deliberately not decided inside that card.
Observation-class finding, filed unassigned with no
pm:queue— for triage to grade and route. Handed up by the dev on #7639 / PR #7896 as an explicit "for you to grade, not for me to decide" residual, and graded by the services PM as not a blocker for that PR (reasoning below) but worth its own card.What was measured
packages/runtime/src/domains/automation.ts:135-147applies the #5519anonymous baseline to the whole/automationdomain — not per route, not per field.automation.ts:489-496then answers run-detail withdeps.success(run), handing back theExecutionLogEntryverbatim: no projection, no redaction, no masking, on any field, anywhere on that path. A repo-wide grep for redact/mask/sanitize acrossservice-automationand the domain returns zero non-comment hits.So the only question this surface asks is "are you authenticated?". Any authenticated caller who knows a run id reads whatever that run's log entry holds — including, via the variables snapshot and via
output, the triggering record's fields, without that record's own field-level security applying.Why it is worth a card
The exposure is not new and was not introduced by #7639/PR #7896, which is exactly why it needs its own card rather than a veto on that PR:
outputhas always carried record fields whenever a flow author declared the record (or a projection of it) as an output variable — so the FLS-bypass property predates the paused-variables work.steps, flow definitions and the rest of the/automationread surface sit behind the same single gate.Framing this as "a problem with the
variableskey" would be the inaccurate version. The accurate question is a domain-level policy one:That is a product/security design call with real cost on both sides — narrowing it changes what every existing operator tool can read, and per-field filtering of a free-form variable map is not obviously well-defined (keys are
<nodeId>.<key>,record,previous,$runId, seeded inputs; only some of them correspond to a record field at all).What the other door does today
The identical snapshot has a second, differently gated door:
sys_automation_run.variables_json(sys-automation-run.object.ts:164-169) persists it verbatim for every paused run, and that read goes through the system object's permissions. So the platform currently answers the same question two ways depending on which door you knock on — which is itself the sharpest evidence that a deliberate policy has never been stated for this surface.Explicitly NOT claimed here
recordLogomits them while the continuation keeps them #7639: connector credentials live in the connector registry and are never handed to a flow; thehttpnode'ssigningSecret/headers are config inputs interpolated from variables and never written back (builtin/http-nodes.ts:115-170, output is{response, status}). A secret reaches variables only if a flow author puts one there — and by that same route it reachesoutput. This card is about record-field FLS, not secret leakage.origin/mainbefore PR fix(services): a paused run's variable snapshot is readable on run-detail (#7639) #7896 as well, for any flow whose author declared record data as output.Suggested routing
Landing site is
packages/runtime/src/domains/automation.ts(the gate) and possiblyplugin-security(whatever would supply the record's FLS), so this likely is notdomain:serviceseven though it surfaced there — theservice-automationengine is not where the gate lives. Triage's call.If graded for work, this probably wants
needs-user-decisionbefore implementation rather than a dispatch: the narrowing decision has a real blast radius, and per #4829 the time to put it in the maintainer's inbox is when there is pull — which there now arguably is, since PR #7896 makes the exposure unconditional.Evidence
Measured 2026-08-12 by the dev on PR #7896 (
origin/mainbase68a1edb), readingpackages/runtime/src/domains/automation.ts,packages/services/service-automation/src/engine.ts,builtin/http-nodes.ts, andsys-automation-run.object.ts. Full measurement recorded in the<!-- os-dev-report -->comment on #7639 and in PR #7896's body.Source
Split out of #7639 (PR #7896) — the dev's named residual, deliberately not decided inside that card.