{"id":"GHSA-prj9-97mp-mwh2","summary":"OliveTin has Unvalidated `ot_`-prefixed Arguments that Bypass Input Filtering","details":"### Description\n\nThe `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.\n\nThese bypassed arguments are:\n\n1. **Not type-checked** — the validation loop only iterates over the action's defined arguments, so `ot_`-prefixed arguments skip all type safety checks entirely.\n2. **Set as environment variables** — via `buildEnv()`, with completely unvalidated values, and passed to the executed command.\n3. **Included in the template context** — available as `.Arguments.ot_*` in template rendering.\n\n### Affected Code\n\n**Filter bypass — `service/internal/executor/executor.go` (lines 728–731):**\n\n```go\nfunc keepArgument(name string, definedNames map[string]struct{}) bool {\n    _, ok := definedNames[name]\n    return ok || strings.HasPrefix(name, \"ot_\")\n}\n```\n\n**System args only override two keys — `service/internal/executor/executor.go` (lines 742–745):**\n\n```go\nfunc injectSystemArgs(req *ExecutionRequest) {\n    req.Arguments[\"ot_executionTrackingId\"] = req.TrackingID\n    req.Arguments[\"ot_username\"] = req.AuthenticatedUser.Username\n}\n```\n\nAny other `ot_`-prefixed argument (e.g., `ot_malicious`) survives both functions.\n\n**Unvalidated values become environment variables — `service/internal/executor/executor.go` (lines 867–882):**\n\n```go\nfunc buildEnv(args map[string]string) []string {\n    ret := append(os.Environ(), \"OLIVETIN=1\")\n    for k, v := range args {\n        varName := fmt.Sprintf(\"%v\", strings.TrimSpace(strings.ToUpper(k)))\n        if varName == \"\" { continue }\n        ret = append(ret, fmt.Sprintf(\"%v=%v\", varName, v))\n    }\n    return ret\n}\n```\n\nThe value `v` is never validated. It can contain newlines, shell metacharacters, null bytes, or any arbitrary data.\n\n### Proof of Concept\n\nAn attacker sends a `StartAction` request with extra `ot_`-prefixed arguments:\n\n```json\n{\n  \"bindingId\": \"\u003cany-action-id\u003e\",\n  \"arguments\": [\n    { \"name\": \"ot_custom_var\", \"value\": \"arbitrary unvalidated content \\n with newlines\" },\n    { \"name\": \"ot_another\",    \"value\": \"$(whoami)\" }\n  ]\n}\n```\n\nThese arguments:\n\n- Pass through `filterToDefinedArgumentsOnly` (the `ot_` prefix exempts them).\n- Are never type-checked (not in the action's argument definitions).\n- Become environment variables `OT_CUSTOM_VAR` and `OT_ANOTHER` in the executed command's environment.\n- Are available in the template rendering context as `.Arguments.ot_custom_var` and `.Arguments.ot_another`.\n\n### Impact\n\n- **Environment variable pollution** — attacker can set arbitrary environment variables (with `OT_` uppercased prefix) in the execution environment of any action they can trigger. Scripts or programs that read custom environment variables could be influenced.\n- **Potential for secondary exploitation** — if any executed script or command reads `OT_`-prefixed environment variables, the unvalidated content could cause unexpected behavior.\n- **Template context pollution** — although Go's `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.\n\n### Suggested Fix\n\nRemove the `ot_` prefix exception from `keepArgument`, or restrict it to only the two known system arguments:\n\n```go\nvar systemArgs = map[string]struct{}{\n    \"ot_executionTrackingId\": {},\n    \"ot_username\":            {},\n}\n\nfunc keepArgument(name string, definedNames map[string]struct{}) bool {\n    _, isDefined := definedNames[name]\n    _, isSystem := systemArgs[name]\n    return isDefined || isSystem\n}\n```\n\n---\n\n## Discovery Methodology\n\nBoth vulnerabilities were identified through manual source code review of the OliveTin repository, focusing on:\n\n- Input validation boundaries (API request fields flowing into file system operations and execution contexts)\n- Argument filtering and type-checking logic in the executor\n- File path construction in the log persistence feature\n\nNo automated scanners or fuzzing tools were used. The review was conducted against the current `main` branch source code.\n\n---","aliases":["CVE-2026-53541","GO-2026-5552"],"modified":"2026-06-25T23:11:17.575984644Z","published":"2026-06-24T17:43:50Z","database_specific":{"severity":"MODERATE","github_reviewed":true,"github_reviewed_at":"2026-06-24T17:43:50Z","nvd_published_at":null,"cwe_ids":["CWE-20"]},"references":[{"type":"WEB","url":"https://github.com/OliveTin/OliveTin/security/advisories/GHSA-prj9-97mp-mwh2"},{"type":"WEB","url":"https://github.com/OliveTin/OliveTin/commit/ebffd9f040f791208aee1db2e5a8aecd1e3e603d"},{"type":"PACKAGE","url":"https://github.com/OliveTin/OliveTin"}],"affected":[{"package":{"name":"github.com/OliveTin/OliveTin","ecosystem":"Go","purl":"pkg:golang/github.com/OliveTin/OliveTin"},"ranges":[{"type":"SEMVER","events":[{"introduced":"0"},{"fixed":"0.0.0-20260531214440-ebffd9f040f7"}]}],"database_specific":{"source":"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/06/GHSA-prj9-97mp-mwh2/GHSA-prj9-97mp-mwh2.json"}}],"schema_version":"1.9.0","severity":[{"type":"CVSS_V3","score":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:N"}]}