The filterToDefinedArgumentsOnly function in the executor is intended to discard any arguments not explicitly defined in the action's configuration. However, a special case allows any argument whose name starts with ot_ to bypass this filter. While two system arguments (ot_executionTrackingId and ot_username) are injected by OliveTin and overridden, all other ot_-prefixed arguments supplied by the user pass through unmodified.
These bypassed arguments are:
ot_-prefixed arguments skip all type safety checks entirely.buildEnv(), with completely unvalidated values, and passed to the executed command..Arguments.ot_* in template rendering.Filter bypass — service/internal/executor/executor.go (lines 728–731):
func keepArgument(name string, definedNames map[string]struct{}) bool {
_, ok := definedNames[name]
return ok || strings.HasPrefix(name, "ot_")
}
System args only override two keys — service/internal/executor/executor.go (lines 742–745):
func injectSystemArgs(req *ExecutionRequest) {
req.Arguments["ot_executionTrackingId"] = req.TrackingID
req.Arguments["ot_username"] = req.AuthenticatedUser.Username
}
Any other ot_-prefixed argument (e.g., ot_malicious) survives both functions.
Unvalidated values become environment variables — service/internal/executor/executor.go (lines 867–882):
func buildEnv(args map[string]string) []string {
ret := append(os.Environ(), "OLIVETIN=1")
for k, v := range args {
varName := fmt.Sprintf("%v", strings.TrimSpace(strings.ToUpper(k)))
if varName == "" { continue }
ret = append(ret, fmt.Sprintf("%v=%v", varName, v))
}
return ret
}
The value v is never validated. It can contain newlines, shell metacharacters, null bytes, or any arbitrary data.
An attacker sends a StartAction request with extra ot_-prefixed arguments:
{
"bindingId": "<any-action-id>",
"arguments": [
{ "name": "ot_custom_var", "value": "arbitrary unvalidated content \n with newlines" },
{ "name": "ot_another", "value": "$(whoami)" }
]
}
These arguments:
filterToDefinedArgumentsOnly (the ot_ prefix exempts them).OT_CUSTOM_VAR and OT_ANOTHER in the executed command's environment..Arguments.ot_custom_var and .Arguments.ot_another.OT_ uppercased prefix) in the execution environment of any action they can trigger. Scripts or programs that read custom environment variables could be influenced.OT_-prefixed environment variables, the unvalidated content could cause unexpected behavior.text/template does not recursively evaluate data values (mitigating direct template injection), the extra arguments are accessible in the template context and could interact unexpectedly with custom template logic.Remove the ot_ prefix exception from keepArgument, or restrict it to only the two known system arguments:
var systemArgs = map[string]struct{}{
"ot_executionTrackingId": {},
"ot_username": {},
}
func keepArgument(name string, definedNames map[string]struct{}) bool {
_, isDefined := definedNames[name]
_, isSystem := systemArgs[name]
return isDefined || isSystem
}
Both vulnerabilities were identified through manual source code review of the OliveTin repository, focusing on:
No automated scanners or fuzzing tools were used. The review was conducted against the current main branch source code.
{
"github_reviewed_at": "2026-06-24T17:43:50Z",
"severity": "MODERATE",
"cwe_ids": [
"CWE-20"
],
"nvd_published_at": null,
"github_reviewed": true
}