close
Skip to content

Outbound secret filter denies all secrets for tasks without a Modeler element template #8359

Description

@chillleader

Describe the Bug

The outbound secret filter (secret-filter.mode, added in #7568) only collects secrets from BPMN elements carrying a zeebe:modelerTemplate attribute:

// ProcessDefinitionSecretKeyCache.retrieveOutboundEligibleElementsFromProcess
if (iet.isInstance(element) && isElementTemplate(element)) {

A task without that attribute never enters the map, so getSecretKeys returns Collections.emptyList() and SecretFilter.allowOnly(List.of()) denies every name — as its Javadoc states, "An empty list denies all secrets."

The symptom is a wrong value rather than an error. SecretHandler returns null for a denied name and SecretUtil.resolveSecretValue returns matcher.group(), so the literal {{secrets.MY_SECRET}} is bound as the credential and sent to the target. Users see an authentication failure from the remote system; the only local trace is a DEBUG line.

LAX does not mitigate this. It allows all only when the lookup throwsLazyLoadingSecretFilter does secretNames = names != null ? Set.copyOf(names) : null. An empty list is a successful lookup, so LAX and STRICT behave identically here.

Steps to Reproduce

  1. Add a service task whose zeebe:taskDefinition type is a runtime-served connector (e.g. io.camunda:http-json:1) without applying an element template, so it has no zeebe:modelerTemplate attribute — hand-written or generated BPMN, or setting the type directly.
  2. Reference {{secrets.MY_SECRET}} from one of its zeebe:input mappings, with a provider that can resolve it.
  3. Set camunda.connector.secret-resolver.secret-filter.mode=STRICT (or LAX) and run the process.

Expected Behavior

The secret resolves. Whether a secret is allowed should depend on what the element declares, not on how it was authored — zeebe:modelerTemplate records authoring provenance, not function.

Compare the inbound side: ProcessDefinitionInspector identifies a connector by rawProperties.containsKey(INBOUND_TYPE_KEYWORD), the functional marker, and uses getElementTemplateDetails only for metadata — it has no isElementTemplate predicate. ProcessDefinitionSecretKeyCache is the only place the attribute gates anything, and the // pre-existing, move to util from ProcessDefinitionInspector comment on the copied accessor suggests the predicate was built on top of code that never filtered on it. ProcessDefinitionSecretKeyCacheTest.getSecretKeys_taskWithoutTemplate_excluded_returnsEmptyList pins the current behaviour, but no comment, PR description or issue records why.

Suggested Fix

Drop && isElementTemplate(element), delete the then-unused isElementTemplate and getElementTemplateDetails from that class, and invert the test above.

Safe by construction: the allow-list only ever permits, and it is keyed per element id and built from that element's own input mappings, so scanning more elements cannot admit a secret the model did not declare.

Why now

secret-filter.mode ships DISABLED, so no default deployment is affected. It becomes user-visible once the filter is switched on — planned for SaaS under #8222, with STRICT enabled by default. Fixing this is a prerequisite for that rollout.

Environment

Metadata

Metadata

Assignees

Type

No type

Fields

Urgency

planned

Projects

Status
No status

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions