Skip to content

[8.19](backport #7009) fix: enforce policy-based access control on artifact downloads#7164

Open
mergify[bot] wants to merge 1 commit into
8.19from
mergify/bp/8.19/pr-7009
Open

[8.19](backport #7009) fix: enforce policy-based access control on artifact downloads#7164
mergify[bot] wants to merge 1 commit into
8.19from
mergify/bp/8.19/pr-7009

Conversation

@mergify

@mergify mergify Bot commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

What is the problem this PR solves?

The artifact download endpoint (/api/fleet/artifacts/{id}/{sha256}) only validates the agent's API key but never checks whether the requested artifact belongs to the agent's assigned policy. This means an agent enrolled under one policy can download artifacts belonging to a different policy if it knows the artifact ID and SHA256 hash. For example, an agent enrolled under a policy with no integrations can retrieve Elastic Defend trust lists, exception lists, and other security artifacts from another policy.

How does this PR solve the problem?

Implements the authorizeArtifact() function (previously a no-op that returned nil) to enforce policy-based access control:

  1. Adds a GetPolicy(ctx, policyID) method to the policy.Monitor interface that returns the cached policy for a given ID (reloads from ES on cache miss).
  2. In authorizeArtifact, fetches the agent's policy via the monitor using agent.AgentPolicyID and verifies that the requested artifact (identifier + decoded_sha256) appears in the policy's inputs[].artifact_manifest.artifacts.
  3. Returns 403 Forbidden (ErrUnauthorizedArtifact) if the artifact is not listed in the agent's assigned policy.

How to test this PR locally

  1. Set up Fleet Server with Elasticsearch and Kibana
  2. Create two agent policies: Victim-Policy with Elastic Defend integration (add a trusted application), and Attacker-Policy with no integrations
  3. Create an enrollment token for Attacker-Policy and enroll an agent
  4. Attempt to download an artifact belonging to Victim-Policy using the attacker agent's API key — should now receive 403 Forbidden instead of the artifact contents
  5. Verify that an agent enrolled under Victim-Policy can still download its own artifacts normally (200 OK)

Design Checklist

  • I have ensured my design is stateless and will work when multiple fleet-server instances are behind a load balancer.
  • I have or intend to scale test my changes, ensuring it will work reliably with 100K+ agents connected.
  • I have included fail safe mechanisms to limit the load on fleet-server: rate limiting, circuit breakers, caching, load shedding, etc.

Checklist

* fix: enforce policy-based access control on artifact downloads

The artifact download endpoint (/api/fleet/artifacts/{id}/{sha256})
previously only validated the agent's API key but never checked whether
the requested artifact belonged to the agent's assigned policy. This
allowed an agent enrolled under one policy to download artifacts from
a different policy if it knew the artifact ID and SHA256 hash.

Add authorizeArtifact implementation that fetches the agent's policy
from the in-memory policy monitor cache and verifies the requested
artifact appears in the policy's artifact_manifest before serving it.
Returns 403 Forbidden if the artifact is not in the agent's policy.

Resolves: https://github.com/elastic/security/issues/8396

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* chore: add changelog fragment for artifact access control fix

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* docs: document race condition tradeoffs in authorizeArtifact

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: use any instead of interface{} per Go conventions

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor: add typed ArtifactManifest struct for policy input parsing

Defines model.ArtifactManifest and model.ManifestEntry structs so
policyHasArtifact no longer navigates untyped map[string]any chains.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
(cherry picked from commit caa8b2d)

# Conflicts:
#	internal/pkg/api/handleCheckin_test.go
#	internal/pkg/policy/monitor.go
@mergify mergify Bot requested a review from a team as a code owner June 5, 2026 07:24
@mergify mergify Bot added backport conflicts There is a conflict in the backported pull request labels Jun 5, 2026
@mergify mergify Bot requested review from swiatekm and ycombinator June 5, 2026 07:24
@mergify mergify Bot added backport conflicts There is a conflict in the backported pull request labels Jun 5, 2026
@mergify

mergify Bot commented Jun 5, 2026

Copy link
Copy Markdown
Contributor Author

Cherry-pick of caa8b2d has failed:

On branch mergify/bp/8.19/pr-7009
Your branch is up to date with 'origin/8.19'.

You are currently cherry-picking commit caa8b2d.
  (fix conflicts and run "git cherry-pick --continue")
  (use "git cherry-pick --skip" to skip this patch)
  (use "git cherry-pick --abort" to cancel the cherry-pick operation)

Changes to be committed:
	new file:   changelog/fragments/1778540235-fix-artifact-access-control.yaml
	modified:   internal/pkg/api/error.go
	modified:   internal/pkg/api/handleArtifacts.go
	new file:   internal/pkg/api/handleArtifacts_test.go
	modified:   internal/pkg/dl/constants.go
	modified:   internal/pkg/server/fleet.go
	modified:   testing/e2e/api_version/client_api_2023_06_01.go
	modified:   testing/e2e/api_version/client_api_current.go
	modified:   testing/e2e/scaffold/scaffold.go

Unmerged paths:
  (use "git add <file>..." to mark resolution)
	both modified:   internal/pkg/api/handleCheckin_test.go
	both modified:   internal/pkg/policy/monitor.go

To fix up this pull request, you can check it out locally. See documentation: https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/reviewing-changes-in-pull-requests/checking-out-pull-requests-locally

@github-actions github-actions Bot added bug Something isn't working Team:Elastic-Agent-Control-Plane Label for the Agent Control Plane team labels Jun 5, 2026
@github-actions

github-actions Bot commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

TL;DR

check-ci failed because unresolved cherry-pick conflict markers (<<<<<<<, =======, >>>>>>>) were committed in Go source files, so goimports/parser exited before CI could proceed. Resolve the conflicts in the two affected files and rerun CI.

Remediation

  • Manually resolve merge conflicts in internal/pkg/api/handleCheckin_test.go and internal/pkg/policy/monitor.go, removing all conflict markers and keeping the intended backport logic.
  • Run mage check:all (or at minimum the same check_ci.sh path) locally after conflict resolution to confirm parsing/linting succeeds.
Investigation details

Root Cause

This is a configuration/backport integration failure from an incomplete cherry-pick resolution, not a runtime code regression.

Build logs show Go parser errors in both files at lines containing conflict-marker tokens, e.g. expected declaration, found '<<' and illegal character U+0023 '#'.

The PR already contains Mergify’s conflict note indicating these exact files were left unmerged:

  • internal/pkg/api/handleCheckin_test.go
  • internal/pkg/policy/monitor.go

Evidence

internal/pkg/api/handleCheckin_test.go:44:1: expected declaration, found '<<'
internal/pkg/api/handleCheckin_test.go:76:1: expected declaration, found '>>'
internal/pkg/api/handleCheckin_test.go:76:82: illegal character U+0023 '#'
internal/pkg/policy/monitor.go:68:1: expected '}', found '<<'
internal/pkg/policy/monitor.go:77:82: illegal character U+0023 '#'
internal/pkg/policy/monitor.go:535:1: expected declaration, found '<<'
Error: running "go tool -modfile dev-tools/go.mod golang.org/x/tools/cmd/goimports -w ." failed with exit code 2

Verification

  • Local rerun not performed in this detective workflow (read-only diagnosis from provided Buildkite artifacts).

Follow-up

After conflict resolution, if CI still fails, re-check for any remaining conflict markers with:
rg '<<<<<<<|=======|>>>>>>>' internal/pkg/api/handleCheckin_test.go internal/pkg/policy/monitor.go


What is this? | From workflow: PR Buildkite Detective

Give us feedback! React with 🚀 if perfect, 👍 if helpful, 👎 if not.

@mergify

mergify Bot commented Jun 8, 2026

Copy link
Copy Markdown
Contributor Author

This pull request has not been merged yet. Could you please review and merge it @ycombinator? 🙏

1 similar comment
@mergify

mergify Bot commented Jun 15, 2026

Copy link
Copy Markdown
Contributor Author

This pull request has not been merged yet. Could you please review and merge it @ycombinator? 🙏

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backport bug Something isn't working conflicts There is a conflict in the backported pull request Team:Elastic-Agent-Control-Plane Label for the Agent Control Plane team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant