Graylog2 / Graylog2/graylog2-server
Output configuration fields marked isEncrypted are stored in plain text when installed from a content pack
- Dominant language
- Java
- Stars
- 8.1k
- Forks
- 1.1k
- Avg merge
- 1d 20h
- Merged PRs (30d)
- 217
Description
## Expected Behavior
`MessageOutput` configuration fields declared with `isEncrypted=true` should be encrypted before being persisted, no matter which code path creates the output. Content pack installation should behave like the REST API.
## Current Behavior
`OutputFacade.decode` passes the raw configuration map straight into `outputService.create`, with no conversion of encrypted fields:
```java
final CreateOutputRequest createOutputRequest = CreateOutputRequest.create(
outputEntity.title().asString(parameters),
outputEntity.type().asString(parameters),
toValueMap(outputEntity.configuration(), parameters),
null // Outputs are assigned to streams in StreamFacade
);
```
`InputFacade.decode` does the equivalent conversion, routing the configuration through `ConfigurationWrapper` so the specialized deserializer turns plain strings into `EncryptedValue` objects:
```java
// Incoming encrypted fields from a content pack will be plain strings in the input configuration at this
// point.
// We use the object mapper to convert them into proper EncryptedValue objects. This works, because classes
// implementing the WithInputConfiguration interface will be converted using a specialized deserializer.
final var configuration = objectMapper.convertValue(
new ConfigurationWrapper(type, rawConfiguration), ConfigurationWrapper.class).configuration();
```
Result for outputs: the value lands in MongoDB as a plain `String`. `Configuration` only routes `EncryptedValue` instances into its encrypted map, so `Configuration.getEncryptedValue(key)` returns unset and the running output gets no secret.
Two concrete consequences:
1. A content pack that parameterizes the field stores the operator supplied secret in plain text in the `outputs` collection.
2. A content pack exported from a live system carries the placeholder that `ValueReference.of(EncryptedValue)` substitutes (``). Installing it stores that literal placeholder string as the field value rather than leaving the field unset.
The export side itself is fine. `ValueReference.of(EncryptedValue)` already prevents secrets from leaking into an exported content pack.
## Steps to Reproduce
1. Build a plugin output with a `TextField` declared `isEncrypted=true`, as in #26626.
2. Create a content pack containing that output, with the encrypted field exposed as a parameter.
3. Install the content pack and supply a value for the parameter.
4. Inspect the document in the `outputs` collection. The field holds the plain string, not `{ "encrypted_value": "...", "salt": "..." }`.
## Context
Follow-up to #26626 and PR #27104, which fixed the REST API create and update paths (`OutputResource`) and the load path (`OutputServiceImpl`). The content pack path was left out of scope there.
No output shipped by core or enterprise declares `isEncrypted` today, so this only affects third-party plugin outputs. It is the same class of bug as #26626 and the fix is to mirror what `InputFacade.decode` already does.
## Related
- #26626
- #27104
Contributor guide
Research direction
Start at OutputFacade.decode and compare its output configuration handling with InputFacade.decode, especially the ConfigurationWrapper conversion before outputService.create. Verify that content-pack installation preserves encrypted fields as encrypted values, leaves exported placeholder values unset rather than storing them literally, and makes Configuration.getEncryptedValue(key) return the supplied secret.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- java
- Domain
- backend, security
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 72/100