Skip to content

fix(installer): gate agent installer command behind INSTALL_AGENT capability - #7985

Draft
Hedi (heditar) wants to merge 20 commits into
mainfrom
heditar-install-agent-capability
Draft

Hedi (heditar) wants to merge 20 commits into
mainfrom
heditar-install-agent-capability

Conversation

@heditar

@heditar Hedi (heditar) commented Sep 15, 2026

Copy link
Copy Markdown
Contributor

Proposed changes

  • created a new resource type "AGENT_INSTALLER" and new capa "INSTALL_AGENT", this capa give access to get install command and executors
  • The link to go to the install page is now gated by this capa and is only displayed for users having this capa
  • This capa has been added to the servcie account
  • The install command now include the service token in the header of the curl command

PR for the agents: OpenAEV-Platform/agent#226

Testing Instructions

See with Hedi (@heditar)

Tested install and upgrade on mac

Related issues

  • Related OpenAEV-Platform/filigran-private#320

Checklist

  • I consider the submitted work as finished
  • [] I tested the code for its functionality -> -> STILL BEING TESTED
  • I wrote test cases for the relevant uses case
  • I added/update the relevant documentation (either on github or on notion)
  • Where necessary I refactored code to improve the overall quality
  • For bug fix -> I implemented a test that covers the bug

…ability (OpenAEV-Platform/filigran-private#320)

- Add ResourceType.AGENT_INSTALLER and Capability.INSTALL_AGENT (group
  TARGETS, scope TENANT) so the agent installer command is no longer
  reachable without a dedicated capability.
- Require INSTALL_AGENT on ExecutorApi#getOpenAevAgentInstaller instead
  of skipping RBAC, since that endpoint returns an install command
  embedding a service-account bearer token.
- Hide the "Install simulation agents" TopBar button unless the user
  holds INSTALL_AGENT.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@github-actions

github-actions Bot commented Sep 15, 2026

Copy link
Copy Markdown
Contributor

📖 Documentation check — ❌ Failed

18 functional file(s) changed, 0 documentation file(s) changed.

Blocking gaps

Update the docs or add the No need documentation label to bypass.

🟠 1 new endpoint(s) in existing controller

  • File: openaev-api/src/main/java/io/openaev/rest/executor/ExecutorApi.java
  • Expected doc update: docs/docs/usage/rest-api.md
  • Reason: New endpoints expand the API surface.

Add the No need documentation label to bypass this check when no documentation update is needed.

@Filigran-Automation Filigran Automation (Filigran-Automation) added the filigran team Item from the Filigran team. label Sep 15, 2026
…penAEV-Platform/filigran-private#320)

Display it in the capabilities screen under Security rather than
Targets, since it gates a security-sensitive endpoint.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@codecov

codecov Bot commented Sep 15, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 0% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 46.16%. Comparing base (b906105) to head (33969ed).
⚠️ Report is 2 commits behind head on main.

Files with missing lines Patch % Lines
openaev-front/src/admin/components/nav/TopBar.tsx 0.00% 2 Missing ⚠️

❌ Your patch check has failed because the patch coverage (0.00%) is below the target coverage (80.00%). You can increase the patch coverage or adjust the target coverage.
❌ Your project check has failed because the head coverage (11.57%) is below the target coverage (80.00%). You can increase the head coverage or adjust the target coverage.

Additional details and impacted files
@@             Coverage Diff              @@
##               main    #7985      +/-   ##
============================================
- Coverage     46.80%   46.16%   -0.64%     
- Complexity    12404    12406       +2     
============================================
  Files          3039     3015      -24     
  Lines        101275   100972     -303     
  Branches      16390    16264     -126     
============================================
- Hits          47401    46616     -785     
- Misses        50066    50629     +563     
+ Partials       3808     3727      -81     
Flag Coverage Δ
backend 46.16% <0.00%> (-0.64%) ⬇️
e2e 46.16% <0.00%> (-0.64%) ⬇️
frontend 46.16% <0.00%> (-0.64%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Hedi (heditar) and others added 8 commits September 15, 2026 15:38
…enAEV-Platform/filigran-private#320)

Capability labels shown in the capabilities screen are looked up by
their raw enum value, so add "INSTALL_AGENT" to every locale.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
… capability (#320)

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
#320)

Gates the raw executable and Windows package download endpoints
(getOpenAevAgentExecutable, getOpenAevAgentPackage) behind the
AGENT_RUNTIME_ACCESS capability, which now also grants (AGENT, READ).
Previously these endpoints skipped RBAC entirely.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
… downloads (#320)

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…vice role capability (#320)

- replace V20260915_Service_Account_Install_Agent datapack with a Flyway
  migration that backfills INSTALL_AGENT on the existing 'Service
  integration' role, matching the pattern used by other role-capability
  migrations
- update ExecutorApiTest to require INSTALL_AGENT (not AGENT_RUNTIME_ACCESS)
  for the executable/package download endpoints, matching the
  AGENT_INSTALLER resource type they now check

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@heditar Hedi (heditar) changed the title fix(security): gate agent installer command behind INSTALL_AGENT capability fix(installer): gate agent installer command behind INSTALL_AGENT capability Sep 16, 2026
- mvn spotless:apply on ExecutorApi.java/ExecutorApiTest.java
- regenerate api-types.d.ts to include INSTALL_AGENT capability and
  AGENT_INSTALLER resource type enum values

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
},
produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
@AccessControl(skipRBAC = true)
@AccessControl(actionPerformed = Action.READ, resourceType = ResourceType.AGENT_INSTALLER)

@laugiov Laurent Giovannoni (laugiov) Sep 16, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we are closing the agent binary endpoints, the implant binary should probably follow.

/api/implant/openaev/{platform}/{architecture} in InjectorApi is still permitAll, with accesscontrol(skipRBAC = true) and a // Public API comment. It is the same kind of endpoint, so leaving one protected and the other open is hard to justify.

Either we close it in the same wave, or we document why the two differ.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes I can do that, If it is ok for you I will do it in a follow up PR to keep the scope of this one small

Hedi (heditar) and others added 5 commits September 17, 2026 17:34
…ALL_AGENT

Constants.SERVICE_ROLE_CAPABILITIES now includes INSTALL_AGENT (backfilled
onto the "Service integration" role by the Flyway migration), but this test
still asserted the old two-capability set, failing CI's API Tests shard.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…released ones

git describe on the PR's merge commit resolves to release 3.260917.1, which
already ships V6_20260916173300000 (inject authorisation) and
V6_20260916200000000 (agent status) — both dated after this migration's old
20260916150000000 timestamp. The Migrations Guard correctly flags that as an
insert in the middle of the already-released block. Renaming to
20260917200000000 puts it after everything currently released.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The previous commit renamed the file but the follow-up `git add` silently
no-opped (invalid old-path pathspec aborted the whole command before the
new path was staged), so the committed content still had the class
declared as V6_20260916150000000__... under the new filename — a
filename/public-class-name mismatch that doesn't compile.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
ExecutorApiTest.givenUserWithInstallAgentCapability_shouldReturnInstallerCommand
calls GET /api/agent/installer/openaev/linux/service, which resolves to
agents/openaev-agent/linux/openaev-agent-installer-Testing.sh under the
"local" test profile. Windows had installer fixtures (session/service-user
variants) but linux only had the upgrade-script and raw-binary fixtures —
the installer one was never added, so the lookup threw
"Agent installer version Testing not found".

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…mmand

The install command's <pre> block rendered as soon as installerToken's
useEffect fired, but that fetch is async and the state starts at ''. The
generated command (curl ... -H "Authorization: Bearer ${installerToken}")
therefore briefly carried an empty bearer token, which the E2E test's
"not empty" check doesn't catch since the string itself is non-empty text.
Against the capability-gated installer endpoint, that empty-token command
fails auth silently, so the agent never installs — this is what was timing
out the infra-agent and infra-multitenant E2E specs (agent never registers
an endpoint within the 150s poll).

Show a spinner instead of the command until the token has actually loaded.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The new installer-token flow needs robust failure handling (correct 404 behavior/no-store caching + frontend error state) and the Flyway migration naming should be aligned with repository conventions.

Get a fresh assessment by requesting another Copilot review.

Pull request overview

This PR introduces a new AGENT_INSTALLER resource type and an INSTALL_AGENT capability to restrict access to OpenAEV agent installation assets/commands, updates the frontend to hide the “Install agents” entry point unless the user holds that capability, and backfills the service role so service-account flows continue to work after the gating.

Changes:

  • Add ResourceType.AGENT_INSTALLER and Capability.INSTALL_AGENT, and include it in the per-tenant “Service integration” role.
  • Gate agent installer-related endpoints with @AccessControl(... ResourceType.AGENT_INSTALLER) and add a new endpoint to retrieve the tenant service-account token for copy/paste installer commands.
  • Frontend: conditionally render the agents install entry point; fetch installer token and embed it in the generated install commands; update i18n and generated API types.
File summaries
File Description
openaev-model/src/main/java/io/openaev/database/model/ResourceType.java Adds AGENT_INSTALLER resource type for RBAC checks.
openaev-model/src/main/java/io/openaev/database/model/Capability.java Adds INSTALL_AGENT capability mapped to AGENT_INSTALLER read access.
openaev-front/src/utils/lang/en.json Adds translation for INSTALL_AGENT.
openaev-front/src/utils/lang/fr.json Adds translation for INSTALL_AGENT.
openaev-front/src/utils/lang/de.json Adds translation for INSTALL_AGENT.
openaev-front/src/utils/lang/es.json Adds translation for INSTALL_AGENT.
openaev-front/src/utils/lang/it.json Adds translation for INSTALL_AGENT.
openaev-front/src/utils/lang/ja.json Adds translation for INSTALL_AGENT.
openaev-front/src/utils/lang/ko.json Adds translation for INSTALL_AGENT.
openaev-front/src/utils/lang/ru.json Adds translation for INSTALL_AGENT.
openaev-front/src/utils/lang/zh.json Adds translation for INSTALL_AGENT.
openaev-front/src/utils/api-types.d.ts Updates generated types to include AGENT_INSTALLER and INSTALL_AGENT.
openaev-front/src/admin/components/nav/TopBar.tsx Hides the “Install simulation agents” link unless the user holds INSTALL_AGENT.
openaev-front/src/admin/components/agents/InstructionSelector.tsx Fetches installer token and uses it in copy/paste install commands; adds loading UI.
openaev-front/src/actions/executors/executor-action.ts Adds frontend action to fetch the installer token endpoint.
openaev-api/src/main/java/io/openaev/service/account/Constants.java Adds INSTALL_AGENT to the service role capability set.
openaev-api/src/main/java/io/openaev/rest/executor/ExecutorApi.java Gates installer/package/executable endpoints; adds new installer-token endpoint.
openaev-api/src/main/java/io/openaev/migration/V6_20260917200000000__Add_install_agent_capability_to_service_role.java Migration to backfill INSTALL_AGENT into the “Service integration” role.
openaev-api/src/test/java/io/openaev/api/capabilities/CapabilityTreeBuilderTest.java Updates expectations for service role capabilities.
openaev-api/src/test/java/io/openaev/rest/ExecutorApiTest.java Updates/extends tests for new capability gating.
openaev-api/src/test/resources/agents/openaev-agent/linux/openaev-agent-installer-Testing.sh Adds a test resource for installer script resolution.
Review details
  • Files reviewed: 20/21 changed files
  • Comments generated: 3
  • Review effort level: Lite

💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread openaev-api/src/main/java/io/openaev/rest/executor/ExecutorApi.java
Comment on lines +71 to +75
useEffect(() => {
fetchOpenAevAgentInstallerToken(tenantPrefix).then(({ data }) => {
setInstallerToken(data);
});
}, [tenantPrefix]);
Comment on lines +16 to +18
@Component
public class V6_20260917200000000__Add_install_agent_capability_to_service_role
extends BaseJavaMigration {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few things before I approve.

Copilot's blocking comments need addressing, except the migration naming one which is a false positive: every V6 migration here uses Sentence_case_with_underscores, yours is consistent.

On tests: everything new uses @WithMockUser, so nothing covers the anonymous call, which is what this PR closes. And the token endpoint has no test at all.

Last, AI told me that it clashes with #8009 on SERVICE_ROLE_CAPABILITIES, and both add a migration on roles_capabilities. End state should be AGENT_RUNTIME_ACCESS + AGENT_DOCUMENT_ACCESS + INSTALL_AGENT. Worth agreeing who merges first, a bad resolution drops a capability silently.

@impolitepanda

Yann (impolitepanda) commented Sep 18, 2026

Copy link
Copy Markdown
Member

A few things before I approve.

Copilot's blocking comments need addressing, except the migration naming one which is a false positive: every V6 migration here uses Sentence_case_with_underscores, yours is consistent.

On tests: everything new uses @WithMockUser, so nothing covers the anonymous call, which is what this PR closes. And the token endpoint has no test at all.

Last, AI told me that it clashes with #8009 on SERVICE_ROLE_CAPABILITIES, and both add a migration on roles_capabilities. End state should be AGENT_RUNTIME_ACCESS + AGENT_DOCUMENT_ACCESS + INSTALL_AGENT. Worth agreeing who merges first, a bad resolution drops a capability silently.

My apologies, I saw this comment right after merging #8009 :( If I need to rollbackn tell me but it would imply a rollback of the implant and oaev backend

Hedi (heditar) and others added 2 commits September 18, 2026 17:27
Added error handling for token retrieval and updated response headers.

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
@heditar

Copy link
Copy Markdown
Contributor Author

A few things before I approve.
Copilot's blocking comments need addressing, except the migration naming one which is a false positive: every V6 migration here uses Sentence_case_with_underscores, yours is consistent.
On tests: everything new uses @WithMockUser, so nothing covers the anonymous call, which is what this PR closes. And the token endpoint has no test at all.
Last, AI told me that it clashes with #8009 on SERVICE_ROLE_CAPABILITIES, and both add a migration on roles_capabilities. End state should be AGENT_RUNTIME_ACCESS + AGENT_DOCUMENT_ACCESS + INSTALL_AGENT. Worth agreeing who merges first, a bad resolution drops a capability silently.

My apologies, I saw this comment right after merging #8009 :( If I need to rollbackn tell me but it would imply a rollback of the implant and oaev backend

I merged your change already

@github-actions

github-actions Bot commented Sep 18, 2026

Copy link
Copy Markdown
Contributor

Container vulnerability scan — Passed

Previously reported findings are no longer present.

Image Critical High Total Status
Standard 0 0 0 ✅ Clear
UBI9 0 0 0 ✅ Clear

View workflow run · Standard JSON report · UBI9 JSON report

Updated from CI run attempt 2.

Hedi (heditar) and others added 2 commits September 18, 2026 17:42
…lation

A merge artifact left an extra closing parenthesis on
SERVICE_ROLE_CAPABILITIES's initializer, breaking the build entirely.

test(executor): verify agent endpoints reject fully unauthenticated requests

None of the existing coverage exercised a request with zero authentication
(only "authenticated but missing INSTALL_AGENT"). This is exactly the gap
behind the infra-agent/infra-multitenant E2E failures: the currently
released agent install script's internal executable-download curl sends no
Authorization header at all, and gets a 401 from AccessControlAspect before
the capability check even runs. Adds a dedicated test class (no
@WithMockUser, so requests run as truly anonymous) asserting 401 on the
executable, package, installer-command and installer-token endpoints.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…token

Drops the executable-download case (redundant with package/installer for
regression-guard purposes) and documents, in the class Javadoc, why these
stay in their own file: ExecutorApiTest carries a class-level @WithMockUser,
and its custom listener authenticates the request through a channel that
SecurityContextHolder.clearContext(), .with(anonymous()), and an explicit
fresh MockHttpSession all failed to override (verified with debug
instrumentation — the test thread's SecurityContextHolder was null on both
sides of the call, yet the response still carried the mock user's identity).
Omitting the annotation from the whole class hierarchy, as here, is the only
approach that reliably reproduces zero authentication.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

filigran team Item from the Filigran team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants