Summary
OliveTin: StartActionAndWait Endpoints Bypass logs Permission and Return Action Output
The synchronous execution RPCs StartActionAndWait and StartActionByGetAndWait return the full LogEntry for the just-executed action without checking whether the caller is allowed to read that action's logs.
OliveTin's ACL model separates exec from logs. A deployment can intentionally allow a user to run an action while denying access to its historical or live output. That separation is enforced in GetLogs, GetActionLogs, ExecutionStatus, and EventStream, but it is not enforced in the synchronous ...AndWait endpoints.
As a result, any user who can execute an action through these endpoints can read the action output immediately even when the action's ACL explicitly sets logs:false.
Details
OliveTin defines separate per-action permissions:
// service/internal/config/config.go
type PermissionsList struct {
View bool `koanf:"view"`
Exec bool `koanf:"exec"`
Logs bool `koanf:"logs"`
Kill bool `koanf:"kill"`
}
The normal log and streaming paths correctly enforce logs permission:
// service/internal/api/api.go
func (api *oliveTinAPI) isLogEntryAllowed(e *executor.InternalLogEntry, user *authpublic.AuthenticatedUser) bool {
if user == nil || !isValidLogEntry(e) {
return false
}
return acl.IsAllowedLogs(api.cfg, user, e.Binding.Action)
}
That check is used by:
GetLogsGetActionLogsExecutionStatusEventStream
However, the synchronous execution endpoints directly return the created LogEntry without any logs ACL check:
// service/internal/api/api.go
func (api *oliveTinAPI) StartActionAndWait(ctx ctx.Context, req *connect.Request[apiv1.StartActionAndWaitRequest]) (*connect.Response[apiv1.StartActionAndWaitResponse], error) {
...
internalLogEntry, ok := api.startActionAndWaitRun(binding, args, user)
if !ok {
return nil, connect.NewError(connect.CodeNotFound, fmt.Errorf("execution not found"))
}
return connect.NewResponse(&apiv1.StartActionAndWaitResponse{
LogEntry: api.internalLogEntryToPb(internalLogEntry, user),
}), nil
}
func (api *oliveTinAPI) StartActionByGetAndWait(ctx ctx.Context, req *connect.Request[apiv1.StartActionByGetAndWaitRequest]) (*connect.Response[apiv1.StartActionByGetAndWaitResponse], error) {
...
internalLogEntry, ok := api.executor.GetLog(execReq.TrackingID)
if ok {
return connect.NewResponse(&apiv1.StartActionByGetAndWaitResponse{
LogEntry: api.internalLogEntryToPb(internalLogEntry, user),
}), nil
}
return nil, connect.NewError(connect.CodeNotFound, fmt.Errorf("execution not found"))
}
And internalLogEntryToPb() includes the full output:
// service/internal/api/api.go
func (api *oliveTinAPI) internalLogEntryToPb(logEntry *executor.InternalLogEntry, authenticatedUser *authpublic.AuthenticatedUser) *apiv1.LogEntry {
pble := &apiv1.LogEntry{
ActionTitle: logEntry.ActionTitle,
Output: logEntry.Output,
ExitCode: logEntry.ExitCode,
ExecutionTrackingId: logEntry.ExecutionTrackingID,
...
}
...
return pble
}
The executor separately enforces exec permission, but there is no subsequent check that the response should omit or deny Output when logs:false.
Local Reproduction
I verified this locally with a one-off test against the real StartActionAndWait handler.
Configuration used:
- action
secret_actionwith shellecho SECRET_FROM_ACTION - user
lowmatched by ACL:exec: truelogs: falseview: falsekill: false
Then I invoked StartActionAndWait as low through the real handler path using header-based auth.
Observed output from the test run:
=== RUN TestTempStartActionAndWaitLeaksOutputWithoutLogsPermission
time="2026-03-12T23:36:33+01:00" level=info msg="Action requested" actionTitle=secret-action tags="[]"
time="2026-03-12T23:36:33+01:00" level=info msg="Action parse args - Before" actionTitle=secret-action cmd="echo SECRET_FROM_ACTION"
time="2026-03-12T23:36:33+01:00" level=info msg="Action parse args - After" actionTitle=secret-action cmd="echo SECRET_FROM_ACTION"
time="2026-03-12T23:36:33+01:00" level=info msg="Action started" actionTitle=secret-action timeout=5
time="2026-03-12T23:36:33+01:00" level=info msg="Action finished" actionTitle=secret-action exit=0 outputLength=40 timedOut=false
temp_acl_bypass_test.go:56: output="S\x00E\x00C\x00R\x00E\x00T\x00_\x00F\x00R\x00O\x00M\x00_\x00A\x00C\x00T\x00I\x00O\x00N\x00\r\x00\n\x00" blocked=false action="secret-action"
--- PASS: TestTempStartActionAndWaitLeaksOutputWithoutLogsPermission (0.03s)
The UTF-16LE formatting is from Windows echo, but the key result is that the response returned the real command output even though the caller's ACL explicitly denied log access.
References
service/internal/api/api.goservice/internal/config/config.goservice/internal/acl/acl.go
Impact
- Bypass of log confidentiality controls: Operators may configure actions to be executable but not log-readable. The
...AndWaitendpoints break that separation. - Disclosure of secrets printed by actions: Many OliveTin actions wrap administrative scripts and commands whose stdout/stderr may contain credentials, internal paths, hostnames, tokens, or sensitive operational data.
- Adjacent to previous output-leak bugs: OliveTin already had an EventStream output disclosure bug. This is a separate response-path authorization gap on the synchronous execution APIs.
The application does not correctly enforce access controls, allowing a principal to access resources or operations beyond their granted permissions. Typical impact: unauthorized data access or execution of privileged operations.
CVE-2026-67439 has a CVSS score of 4.3 (Medium). The vector is network-reachable, low privileges required, and no user interaction. A CVSS score reflects the worst-case severity of the vulnerability, not your specific exposure. Whether this affects your application depends on whether the vulnerable code is present and reachable in your environment. A fixed version is available (0.0.0-20260708085316-e421780c9885); upgrading removes the vulnerable code path.
Affected versions
Security releases
Kodem intelligence
Severity tells you how bad this could be in the worst case. It does not tell you whether you are exposed. Exploitability and impact are functions of runtime truth: whether the vulnerable code is present, reachable, and actually executes in your application. A vulnerable package can sit in your dependency tree and never run.
Kodem, an Intelligent Application Security platform, uses runtime intelligence to reveal which vulnerabilities actually execute in production, so teams prioritize the ones that genuinely matter. Kodem's runtime-powered SCA identifies whether this CVE is reachable in your applications.
Already deployed Kodem?
See it in your environmentNew to Kodem? Get a demo →Remediation advice
Kodem Kai can prioritize this vulnerability in your dependency tree and generate a fix recommendation.
Frequently Asked Questions
- What is CVE-2026-67439? CVE-2026-67439 is a medium-severity incorrect authorization vulnerability in github.com/OliveTin/OliveTin (go), affecting versions < 0.0.0-20260708085316-e421780c9885. It is fixed in 0.0.0-20260708085316-e421780c9885. The application does not correctly enforce access controls, allowing a principal to access resources or operations beyond their granted permissions.
- How severe is CVE-2026-67439? CVE-2026-67439 has a CVSS score of 4.3 (Medium). This score reflects the worst-case severity of the vulnerability, not your specific exposure. Whether it represents real risk in your environment depends on whether the vulnerable code is present and reachable.
- Which versions of github.com/OliveTin/OliveTin are affected by CVE-2026-67439? github.com/OliveTin/OliveTin (go) versions < 0.0.0-20260708085316-e421780c9885 is affected.
- Is there a fix for CVE-2026-67439? Yes. CVE-2026-67439 is fixed in 0.0.0-20260708085316-e421780c9885. Upgrade to this version or later.
- Is CVE-2026-67439 exploitable, and should I be worried? Whether CVE-2026-67439 is exploitable in your environment depends on whether the vulnerable code is present and reachable. A CVSS score is a worst-case rating; it does not account for your specific deployment, configuration, or usage patterns. Kodem, an Intelligent Application Security platform, uses runtime intelligence to show which vulnerabilities actually execute in production, so you can focus on the ones that represent real risk. Get a demo
- What actually determines whether CVE-2026-67439 is exploitable, and how bad it is? Exploitability and impact are not fixed properties of a CVE. They depend on runtime truth: whether the vulnerable code is present, reachable, and actually executes in your application. A high CVSS score on a dependency that never runs is not the same as real risk. Kodem, an Intelligent Application Security platform, uses runtime intelligence to reveal which vulnerabilities actually execute in production, so teams prioritize the ones that genuinely matter.
- How do I fix CVE-2026-67439? Upgrade
github.com/OliveTin/OliveTinto 0.0.0-20260708085316-e421780c9885 or later.