Rename Java packages from com.cameleer3 to com.cameleer, module directories from cameleer3-* to cameleer-*, and all references throughout workflows, Dockerfiles, docs, and pom.xml. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
5.6 KiB
Sensitive Keys Unification + SSE Support + Pattern Matching
Date: 2026-04-14 Status: Approved
Problem
The agent has two separate configuration fields for masking sensitive data in captured payloads:
sensitiveHeaders(default:Authorization,Cookie,Set-Cookie,X-API-Key,X-Auth-Token,Proxy-Authorization)sensitiveProperties(default: empty)
Issues:
- Unnecessary split — a key like
secretTokenshould be masked whether it appears as a Camel header or exchange property. Two lists means double configuration and easy omissions. - Case-sensitive matching —
sensitive.contains(key)is case-sensitive. HTTP headers are case-insensitive by spec, and Camel normalizes inconsistently. A header arriving asauthorization(lowercase) bypasses masking. This is a bug. - No pattern support — only exact key names match. No way to mask
X-Internal-*or anything containingpassword. - No SSE support — sensitive keys can only be set via system properties at startup. The server cannot push updated masking rules at runtime.
Design
1. SensitiveKeyMatcher (new class)
Immutable matcher that splits configured keys into two tiers at construction time:
- Exact keys (no
*or?) — stored inTreeSet(String.CASE_INSENSITIVE_ORDER)for O(log n) case-insensitive lookup. - Glob patterns (contain
*or?) — compiled tojava.util.regex.PatternwithCASE_INSENSITIVEflag. Compiled once, reused.
Match algorithm:
matches(key):
if exactKeys.contains(key) -> true // fast path, no iteration
for each compiled pattern -> match // only reached on exact miss
false
Glob-to-regex conversion:
| Glob | Regex | Use case |
|---|---|---|
* |
.* |
Wildcard |
? |
. |
Single char |
| Other regex metacharacters | Escaped | Safety |
Pattern wrapped with ^...$ and compiled with Pattern.CASE_INSENSITIVE.
Examples:
| Key config | Matches | Does not match |
|---|---|---|
Authorization |
Authorization, authorization, AUTHORIZATION |
MyAuthorization |
X-Internal-* |
X-Internal-Token, x-internal-secret |
X-Public-Token |
*password* |
db-password-hash, PASSWORD, userPassword |
passwd |
*-Secret |
API-Secret, app-secret |
SecretKey |
Thread safety: Immutable after construction. The AtomicReference swap in CameleerAgentConfig handles thread-safe replacement when SSE pushes new keys.
Location: cameleer-core/src/main/java/com/cameleer/core/collector/SensitiveKeyMatcher.java
2. ApplicationConfig change
Add to ApplicationConfig in cameleer-common:
private List<String> sensitiveKeys;
// + getter/setter
Sent by server in config-update SSE payloads. Supports both exact names and glob patterns. @JsonInclude(NON_NULL) means omitting the field leaves the agent's current config unchanged (same semantics as other nullable fields).
3. CameleerAgentConfig change
Replace:
private Set<String> sensitiveHeaders;
private Set<String> sensitiveProperties;
With:
private volatile SensitiveKeyMatcher sensitiveKeyMatcher;
- System property:
cameleer.agent.payload.sensitivekeys(comma-separated, supports globs) - Default:
Authorization,Cookie,Set-Cookie,X-API-Key,X-Auth-Token,Proxy-Authorization - Old properties removed:
sensitiveheadersandsensitivepropertiesare deleted, no backward compatibility. - Getter:
getSensitiveKeyMatcher()returns the current matcher. - Handled in both
applyServerConfig()(startup) andapplyServerConfigWithDiff()(runtime SSE).
4. PayloadCapture change
Both captureHeaders() and captureProperties() use the same matcher:
SensitiveKeyMatcher matcher = config.getSensitiveKeyMatcher();
// ...
if (matcher.matches(key)) {
result.put(key, "***MASKED***");
}
5. No new SSE command
This piggybacks on the existing config-update command. The server includes sensitiveKeys in the ApplicationConfig payload — same pattern as tracedProcessors, compressSuccess, routeRecording, etc.
Server Team Contract
The server needs to:
- Store
sensitiveKeys: List<String>on the application config document. - Include the field in
config-updateSSE payloads when set. - Accept both exact key names and glob patterns (
*,?) in the list. - Omit the field (or send
null) to leave the agent's current keys unchanged. - Send an empty list
[]to clear all masking (agent will mask nothing).
Example config-update payload fragment:
{
"sensitiveKeys": [
"Authorization",
"Cookie",
"Set-Cookie",
"X-API-Key",
"*password*",
"*secret*",
"X-Internal-*"
],
"version": 42,
...
}
Agent ACK will include sensitiveKeys in the change summary if the list differs from current.
Files Changed
| File | Change |
|---|---|
cameleer-core/.../collector/SensitiveKeyMatcher.java |
New — immutable glob matcher |
cameleer-core/.../CameleerAgentConfig.java |
Replace two fields with one SensitiveKeyMatcher, add to applyServerConfig/applyServerConfigWithDiff |
cameleer-core/.../collector/PayloadCapture.java |
Use getSensitiveKeyMatcher().matches() for both headers and properties |
cameleer-common/.../model/ApplicationConfig.java |
Add sensitiveKeys field |
cameleer-agent/.../collector/PayloadCaptureTest.java |
Update mocks, add pattern tests |
cameleer-agent/.../collector/SensitiveKeyMatcherTest.java |
New — unit tests for matcher |
cameleer-agent/.../perf/* |
Update mock stubs |