Skip to content

fix(fleet-mcp): security hardening for the MCP server#47908

Open
nulmete wants to merge 9 commits into
mainfrom
43544-evaluate-application-security-of-fleet
Open

fix(fleet-mcp): security hardening for the MCP server#47908
nulmete wants to merge 9 commits into
mainfrom
43544-evaluate-application-security-of-fleet

Conversation

@nulmete

@nulmete nulmete commented Jun 19, 2026

Copy link
Copy Markdown
Member

Related issue: Relates to #43544

Security review + hardening of the experimental Fleet MCP server. Issues were reproduced against a live dev Fleet; this PR fixes the MCP-layer ones.

Finding Before → After Where
A least privilege No signal about the token's power → startup /me check refuses any non-API-only token (fails closed if unreachable). API-only users can be scoped to specific endpoints/teams and their token revoked. Who-can-do-what is documented (Fleet RBAC), not inferred at runtime. main.go requireAPIOnlyUser; fleet_integration.go WhoAmI
B rate-limit XFF bypass Per-IP keyed on spoofable X-Forwarded-For → rotating it bypassed (0×429). Now MCP_RATE_LIMIT_MODE = global (default) | ip (keyed on TCP peer RemoteAddr, never XFF) → rotating XFF throttles (207×429/250). + MCP_AUTH_TOKEN ≥ 32. rate_limit.go, config.go, main.go
C token egress / SSRF FLEET_BASE_URL unchecked → refuse link-local/metadata at startup; schema fetch blocks cross-host redirects. fleet_integration.go validateFleetBaseURL; schema.go
D error-body leak Raw Fleet JSON (incl. internal uuid) → trimmed, e.g. Fleet API returned HTTP 409: Resource Already Exists. fleet_integration.go fleetErrMsg
E dead code / docs / tests Removed dead GetFleetConfig; README 18→19 / 16→17; added validator/role/rate-limit tests. fleet_integration.go, README.md, *_test.go
F writes auto-runnable Only the advisory destructive=true annotation → fleetMCPInstructions now tells the client to show the SQL + targets and confirm before writes. Advisory, not a server control. mcp_server.go
G abrupt SIGTERM shutdown ListenAndServe blocked with no signal handling → on SIGTERM the process was killed immediately (exit 143), resetting any in-flight connection at once. Now a signal-aware root context (signal.NotifyContext) drains in-flight requests via http.Server.Shutdown (10s cap), logs shutting down, and exits 0 — and the startup /me check + temp-query sweep + stdio loop all honor it. Matters for Render redeploys (SIGTERM). main.go

Key decisions

  • Rate limit: two modes
    • ip keys on the unspoofable TCP peer (for direct-connect internal nets);
    • global (default) suits anything behind a proxy/WAF, where per-client DoS limiting belongs.
  • Writes (create_saved_query and run_live_query) stay enabled. Multi-host live query needs POST /reports → run → delete (no read-only equivalent), so read-only-by-default would break it. Bounded by the token's role (observer ⇒ Fleet 403s writes), the advisory confirm (F), and agent --disable_tables.
  • fleetMCPInstructions / destructive=true are advisory — a prompt-injected or raw client ignores them. The enforceable controls are the token role and --disable_tables.
  • Device-side curl/carves exfil is a Fleet/osquery capability, equally reachable via the UI/fleetctl/REST — not an MCP bug. Comprehensive fix is agent --disable_tables (separate Fleet-server/agent issue).
  • Follow-up (separate PR): backoff / computed Retry-After for the limiter.

Residual attack surface (re: "no new attack vectors")

None of these is a vector the MCP invents beyond what Fleet already exposes:

Config choice Residual vector Mitigation
Non-API-only / admin token Leak = arbitrary osquery everywhere API-only required (refuses to start otherwise); use observer for read-only. As an API-only user it can also be scoped to specific endpoints/teams and its token revoked on leak — neither possible with a UI session token.
Writes + auto-approving client Prompt-injection → device actions Human-in-the-loop; confirm-before-write (F); observer token for read-only deployments
Live query at all curl/carves/file device SSRF + exfil Agent --disable_tables (separate issue) — not an MCP-layer fix
Public SSE, no edge Unauth flood 429s operators (shared bucket — fails safe) ip mode (direct nets), WAF (proxied), or stdio
Misconfigured FLEET_BASE_URL Token sent to wrong host Refused at startup (this PR)

Bottom line: with an API-only observer token (now enforced) over stdio, ip mode, or SSE-behind-an-edge, the MCP adds no new attack vector beyond Fleet's existing live-query capability; the residual curl/carves risk is a Fleet-layer concern tracked separately.

Checklist for submitter

If some of the following don't apply, delete the relevant line.

  • Changes file added for user-visible changes in changes/, orbit/changes/ or ee/fleetd-chrome/changes.
    See Changes files for more information.

Testing

  • Added/updated automated tests

  • QA'd all new/changed functionality manually

Summary by CodeRabbit

  • New Features
    • Added MCP_RATE_LIMIT_MODE to select global or per-IP throttling
  • Improvements
    • Startup now verifies the Fleet API key belongs to an API-only Fleet user
    • Added /healthz endpoint
    • Hardened Fleet base URL validation to prevent token leakage to unsafe hosts
    • Improved rate limiting behavior (429 includes Retry-After: 1; per-IP uses the direct client address host)
    • Signal-driven graceful shutdown and stricter MCP_AUTH_TOKEN length checks
    • More robust schema fetching by blocking cross-host and overly long redirect chains
  • Documentation
    • Expanded/clarified write-operation guidance and confirmation requirements
  • Chores / Tests
    • Updated .env docs/ignores, added developer Makefile helpers, and refreshed rate-limiter/Fleet base URL tests

- Startup token-posture check (WhoAmI -> GET /me) logs the principal and warns
  when FLEET_API_KEY is not API-only or is write-capable (global or per-fleet role).
- Replace the per-IP/X-Forwarded-For rate limiter with a global token-bucket
  (local flood backstop, no client-IP attribution) and enforce MCP_AUTH_TOKEN >= 32 chars
  (the real brute-force defense).
- Refuse a link-local/cloud-metadata FLEET_BASE_URL at startup; schema fetch refuses
  cross-host redirects.
- Trim raw Fleet error bodies returned to the client/LLM.
- Remove dead GetFleetConfig; fix README tool counts (19/17); retain auth+rate-limit
  tests and add TestValidateFleetBaseURL + TestFleetIdentityPrivilege.
@codecov

codecov Bot commented Jun 19, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 67.31%. Comparing base (443d82d) to head (7635abe).
⚠️ Report is 61 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main   #47908      +/-   ##
==========================================
+ Coverage   67.23%   67.31%   +0.07%     
==========================================
  Files        3637     3640       +3     
  Lines      229947   230653     +706     
  Branches    11968    11968              
==========================================
+ Hits       154608   155259     +651     
+ Misses      61454    61432      -22     
- Partials    13885    13962      +77     
Flag Coverage Δ
backend 68.94% <ø> (+0.08%) ⬆️

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.

Generic make targets (build/tools/posture/call/sse) to drive the MCP from any shell (jq optional, no language runtime needed). Add .env to .gitignore so a real FLEET_API_KEY/MCP_AUTH_TOKEN can't be committed via the documented 'cp .env.example .env' workflow.
…onfirmation

- Refuse startup unless FLEET_API_KEY is an API-only Fleet user (fails closed
  if /me can't confirm); still warn-only on write-capability.
- MCP_RATE_LIMIT_MODE selects "global" (default, shared bucket) or "ip" (one
  bucket per TCP peer / RemoteAddr — never reads the spoofable X-Forwarded-For),
  which resolves the Finding C bypass while keeping per-IP for internal networks.
- fleetMCPInstructions now directs clients to surface the SQL + target scope and
  confirm before create_saved_query / run_live_query (advisory client guidance).
Comment on lines -648 to -665
// GetFleetConfig retrieves the Fleet server configuration.
func (fc *FleetClient) GetFleetConfig(ctx context.Context) (map[string]interface{}, error) {
resp, err := fc.makeFleetRequest(ctx, "GET", "/api/v1/fleet/config", nil)
if err != nil {
return nil, fmt.Errorf("failed to get fleet config: %w", err)
}
defer resp.Body.Close()

if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("failed to get fleet config: status code %d", resp.StatusCode)
}

var result map[string]interface{}
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
return nil, fmt.Errorf("failed to decode fleet config: %w", err)
}
return result, nil
}

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This piece of code was unused

…Makefile polish

- Drop the read/write rights inference (privilege(), role/teams parsing) and its
  warning. The startup /me check now ONLY enforces API-only (fails closed) and
  logs a factual confirmation; who-can-do-what is documented (Fleet RBAC), not
  guessed. Removes the obsolete TestFleetIdentityPrivilege.
- README: link Fleet's API-only-user docs; add an MCP_RATE_LIMIT_MODE row and
  Render guidance (use the default `global`; `ip` would collapse to the proxy
  IP); fix a stale per-IP rate-limit note.
- Makefile: note the API-only requirement + MCP_RATE_LIMIT_MODE; `make tools`
  now surfaces a startup error instead of printing nothing.
- Rename checkTokenPosture → requireAPIOnlyUser: it now only enforces the
  API-only requirement (the read/write rights inference was already removed).
- Add a signal-aware root context (SIGINT/SIGTERM) and thread it through the
  startup /me check, the temp-query sweep, and the stdio/SSE serving loops. SSE
  now drains in-flight requests via http.Server.Shutdown (10s cap) before
  exiting. Before: SIGTERM = immediate exit 143, in-flight connections reset.
  After: "shutting down" → drain → exit 0. Matters for Render redeploys.
@nulmete nulmete marked this pull request as ready for review June 19, 2026 18:13
@nulmete nulmete requested a review from a team as a code owner June 19, 2026 18:13
Copilot AI review requested due to automatic review settings June 19, 2026 18:13

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Claude Code Review

This repository is configured for manual code reviews. Comment @claude review to trigger a review and subscribe this PR to future pushes, or @claude review once for a one-time review.

Tip: disable this comment in your organization's Code Review settings.

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.

Pull request overview

Hardens the experimental tools/fleet-mcp server against several security findings by tightening token posture checks, mitigating SSRF/token egress risks, reducing error-body leakage, improving SSE rate limiting semantics, and adding graceful shutdown behavior.

Changes:

  • Enforces stronger startup security posture: API-only Fleet token required, MCP_AUTH_TOKEN minimum length, and link-local/metadata base URL rejection.
  • Reworks SSE rate limiting to support global (default) vs ip modes, removing reliance on X-Forwarded-For.
  • Adds redirect hardening for schema fetches, improves shutdown handling on SIGTERM/SIGINT, updates docs, and expands tests.

Reviewed changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
tools/fleet-mcp/schema.go Refuses cross-host redirects during canonical schema fetch to reduce SSRF/token egress risk.
tools/fleet-mcp/README.md Documents new security model decisions (API-only requirement, rate-limit modes, token strength) and tool count updates.
tools/fleet-mcp/rate_limit.go Introduces global vs ip rate-limiting modes and removes XFF-based client identification.
tools/fleet-mcp/rate_limit_test.go Removes tests tied to prior XFF/trusted-proxy behavior.
tools/fleet-mcp/mcp_server.go Expands client instructions to require explicit operator confirmation before write tools.
tools/fleet-mcp/Makefile Adds local dev/test helpers for stdio tool calls, listing tools, and running SSE.
tools/fleet-mcp/main.go Adds token strength check, API-only startup verification, ctx-aware shutdown, and graceful HTTP server shutdown.
tools/fleet-mcp/fleet_integration.go Adds base URL validation, /me identity check, and trims Fleet error bodies. Removes dead GetFleetConfig.
tools/fleet-mcp/fleet_integration_test.go Updates/extends tests for new rate limiter modes and base URL validation.
tools/fleet-mcp/config.go Adds MCP_RATE_LIMIT_MODE config plumbing.
tools/fleet-mcp/.gitignore Ignores .env for safer local config handling.
tools/fleet-mcp/.env.example Documents new env vars and least-privilege guidance for Fleet tokens.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread tools/fleet-mcp/config.go
Comment thread tools/fleet-mcp/fleet_integration.go
Comment thread tools/fleet-mcp/fleet_integration.go
@coderabbitai

coderabbitai Bot commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 9ca50c4e-6bd0-402a-97c8-cde5071358f4

📥 Commits

Reviewing files that changed from the base of the PR and between 3a8f391 and 7635abe.

📒 Files selected for processing (1)
  • tools/fleet-mcp/render.yaml
✅ Files skipped from review due to trivial changes (1)
  • tools/fleet-mcp/render.yaml

Walkthrough

This PR hardens the Fleet MCP server across several dimensions. The rate limiter in rate_limit.go is refactored from a single per-IP throttler into a dual-mode design (global or ip) selected via a new RateLimitMode config field, with XFF trust removed and TTL-based per-IP eviction added. Fleet client security additions in fleet_integration.go include validateFleetBaseURL (blocks link-local/cloud-metadata SSRF targets), WhoAmI/FleetIdentity (calls /api/v1/fleet/me), and fleetErrMsg (safe error bodies); GetFleetConfig is removed. schema.go gains a same-host redirect policy with hop limits. main.go adds requireAPIOnlyUser startup enforcement, a minimum MCP_AUTH_TOKEN length check, signal-aware context propagation, and graceful SSE shutdown with explicit timeouts. The MCP system prompt gains explicit write-operation confirmation requirements. A new Makefile, expanded .env.example, and .gitignore update round out developer tooling and startup posture validation.

Possibly related issues

  • fleetdm/fleet#43544: Directly addresses security evaluation and improvements by implementing token validation, URL validation, rate limiting modes, startup hardening, and API-only user enforcement for the Fleet MCP server.

Possibly related PRs

  • fleetdm/fleet#44481: Both PRs directly modify tools/fleet-mcp/rate_limit.go to implement request throttling middleware; the current PR adds newRateLimiter with configurable global/ip modes and stricter client IP handling.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 40.91% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'fix(fleet-mcp): security hardening for the MCP server' accurately summarizes the main objective—implementing security hardening for the Fleet MCP server. It is clear, specific, and directly reflects the changeset's primary purpose.
Description check ✅ Passed The pull request description is comprehensive and well-structured. It includes the related issue reference (#43544), a detailed table mapping seven security findings to fixes, key technical decisions, residual attack surface analysis, and a completed checklist with relevant items checked (added/updated tests and manual QA).
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch 43544-evaluate-application-security-of-fleet

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot 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.

🧹 Nitpick comments (1)
tools/fleet-mcp/rate_limit.go (1)

107-156: 💤 Low value

Background sweep goroutine runs indefinitely without a shutdown mechanism.

The sweepLoop goroutine started in newPerIPRateLimiter has no way to be stopped. While this is acceptable for the current server lifecycle (the limiter lives until process exit), it prevents clean shutdown in tests and makes the type unsuitable for reuse in contexts where the limiter might be replaced or stopped.

Consider accepting a context for cancellation if you anticipate needing cleaner teardown in future or for testing.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tools/fleet-mcp/rate_limit.go` around lines 107 - 156, The sweepLoop
goroutine launched in newPerIPRateLimiter runs indefinitely without a way to
stop it, which prevents clean shutdown in tests and makes the perIPRateLimiter
unsuitable for reuse where it might need to be replaced or stopped. Modify
newPerIPRateLimiter to accept a context parameter and pass it to sweepLoop, then
update the sweepLoop method to listen for context cancellation alongside the
ticker—when the context is cancelled (via ctx.Done()), the loop should break and
the goroutine should exit cleanly instead of running forever.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@tools/fleet-mcp/rate_limit.go`:
- Around line 107-156: The sweepLoop goroutine launched in newPerIPRateLimiter
runs indefinitely without a way to stop it, which prevents clean shutdown in
tests and makes the perIPRateLimiter unsuitable for reuse where it might need to
be replaced or stopped. Modify newPerIPRateLimiter to accept a context parameter
and pass it to sweepLoop, then update the sweepLoop method to listen for context
cancellation alongside the ticker—when the context is cancelled (via
ctx.Done()), the loop should break and the goroutine should exit cleanly instead
of running forever.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 06b57476-d1f8-4c33-bc8e-c1eff8f8452c

📥 Commits

Reviewing files that changed from the base of the PR and between 2044c77 and a1206e7.

⛔ Files ignored due to path filters (1)
  • tools/fleet-mcp/README.md is excluded by !**/*.md
📒 Files selected for processing (11)
  • tools/fleet-mcp/.env.example
  • tools/fleet-mcp/.gitignore
  • tools/fleet-mcp/Makefile
  • tools/fleet-mcp/config.go
  • tools/fleet-mcp/fleet_integration.go
  • tools/fleet-mcp/fleet_integration_test.go
  • tools/fleet-mcp/main.go
  • tools/fleet-mcp/mcp_server.go
  • tools/fleet-mcp/rate_limit.go
  • tools/fleet-mcp/rate_limit_test.go
  • tools/fleet-mcp/schema.go
💤 Files with no reviewable changes (1)
  • tools/fleet-mcp/rate_limit_test.go

- /healthz: unauthenticated liveness probe (healthzHandler), carved out ahead of
  auth and the rate limiter via a ServeMux so orchestrators (Render, k8s) can
  always confirm the process is up — even under a flood. /sse and /message still
  go through the full middleware chain unchanged.
- README: note that the SSE listener is plain HTTP, so TLS must be terminated in
  front (Render edge / reverse proxy); document the /healthz path for probes.
Render defaults to a TCP/port-open check; healthCheckPath makes it probe the
new /healthz endpoint so deploys gate on a real 200, not just an open port.
@qodo-free-for-open-source-projects

Copy link
Copy Markdown

CI Feedback 🧐

A test triggered by this PR failed. Here is an AI-generated analysis of the failure:

Action: test-go (integration-enterprise, mysql:8.0.44) / test

Failed stage: Run Go Tests [❌]

Failed test name: TestIntegrationsEnterprise/TestUpgradeCodesFromMaintainedApps

Failure summary:

The action failed because the Go integration test
TestIntegrationsEnterprise/TestUpgradeCodesFromMaintainedApps failed.
- Failure location:
server/service/integration_enterprise_test.go:21484 (error trace also points to
server/datastore/mysql/mysqltest/mysqltest.go:487).
- Error: sql: no rows in result set, indicating
the test expected a database row (likely related to maintained apps/upgrade codes data) but the
query returned no results.
- This caused make test-go to fail (make[1]: *** [Makefile:291:
.run-go-tests] Error 1, overall exit code 2).

Relevant error logs:
1:  Runner name: 'ubuntu-8core-1000928976'
2:  Runner group name: 'default larger runners'
...

1299:  �[36;1mattempt=1�[0m
1300:  �[36;1m�[0m
1301:  �[36;1mwhile [ $attempt -le $max_attempts ]; do�[0m
1302:  �[36;1m  echo "Attempt $attempt of $max_attempts"�[0m
1303:  �[36;1m�[0m
1304:  �[36;1m  # Try to connect to MySQL�[0m
1305:  �[36;1m  if wait_for_mysql "mysql_test"; then�[0m
1306:  �[36;1m    # If MySQL is ready, try to connect to MySQL replica�[0m
1307:  �[36;1m    if wait_for_mysql "mysql_replica_test"; then�[0m
1308:  �[36;1m      # Both are ready, we're done�[0m
1309:  �[36;1m      echo "All MySQL connections successful"�[0m
1310:  �[36;1m      exit 0�[0m
1311:  �[36;1m    fi�[0m
1312:  �[36;1m  fi�[0m
1313:  �[36;1m�[0m
1314:  �[36;1m  # If we get here, at least one connection failed�[0m
1315:  �[36;1m  echo "Failed to connect to MySQL on attempt $attempt"�[0m
1316:  �[36;1m�[0m
1317:  �[36;1m  if [ $attempt -lt $max_attempts ]; then�[0m
1318:  �[36;1m    echo "Restarting containers and trying again..."�[0m
1319:  �[36;1m    restart_containers�[0m
1320:  �[36;1m  else�[0m
1321:  �[36;1m    echo "Maximum attempts reached. Failing the job."�[0m
1322:  �[36;1m    exit 1�[0m
...

1517:  �[32m✓�[0m Integrations enterprise test batch software installer and FMA categories valid categories 1 (0.56s)
1518:  �[32m✓�[0m Integrations enterprise test batch software installer and FMA categories valid categories 2 (0.56s)
1519:  �[32m✓�[0m Integrations enterprise test batch software installer and FMA categories valid categories 3 - security and utilities (0.56s)
1520:  �[32m✓�[0m Integrations enterprise test batch software installer and FMA categories valid categories 4 - mixed with new categories (0.56s)
1521:  �[32m✓�[0m Integrations enterprise test batch software upload with SHAs (8.11s)
1522:  �[32m✓�[0m Integrations enterprise test calendar callback (8.84s)
1523:  �[32m✓�[0m Integrations enterprise test calendar event body update (7.34s)
1524:  �[32m✓�[0m Integrations enterprise test calendar events (5.59s)
1525:  �[32m✓�[0m Integrations enterprise test calendar events transferring hosts (3.89s)
1526:  �[32m✓�[0m Integrations enterprise test cancel batch scripts (0.23s)
1527:  �[32m✓�[0m Integrations enterprise test certificates specs (0.46s)
1528:  �[32m✓�[0m Integrations enterprise test conditional access basic setup (0.28s)
1529:  �[32m✓�[0m Integrations enterprise test conditional access bypass (0.57s)
1530:  �[32m✓�[0m Integrations enterprise test conditional access bypass activity created with default idp full name (0.01s)
1531:  �[32m✓�[0m Integrations enterprise test conditional access bypass activity includes actual IDP name when SCIM user present (0.02s)
1532:  �[32m✓�[0m Integrations enterprise test conditional access bypass bypass allowed when only non-c a critical policy is failing (0.03s)
1533:  �[32m✓�[0m Integrations enterprise test conditional access bypass bypass fails when disabled (0.03s)
1534:  �[32m✓�[0m Integrations enterprise test conditional access bypass bypass fails when host has failing critical policy (0.02s)
1535:  �[32m✓�[0m Integrations enterprise test conditional access bypass bypass succeeds after re-enabling (0.04s)
...

1544:  �[32m✓�[0m Integrations enterprise test create API only user premium (0.13s)
1545:  �[32m✓�[0m Integrations enterprise test create API only user premium allow only a limited number of api endpoints (0.00s)
1546:  �[32m✓�[0m Integrations enterprise test create API only user premium fleet-scoped assignment without api endpoints grants full access (0.02s)
1547:  �[32m✓�[0m Integrations enterprise test create API only user premium global role and fleets together (0.00s)
1548:  �[32m✓�[0m Integrations enterprise test create API only user premium global role with specific api endpoints (0.02s)
1549:  �[32m✓�[0m Integrations enterprise test create API only user premium invalid api endpoint not in catalog (0.00s)
1550:  �[32m✓�[0m Integrations enterprise test create API only user premium missing name (0.00s)
1551:  �[32m✓�[0m Integrations enterprise test create API only user premium neither global role nor fleets (0.00s)
1552:  �[32m✓�[0m Integrations enterprise test create API only user premium nil api endpoints grants full access (0.02s)
1553:  �[32m✓�[0m Integrations enterprise test create API only user premium wildcard mixed with other entries (0.00s)
1554:  �[32m✓�[0m Integrations enterprise test custom transparency URL (0.08s)
1555:  �[32m✓�[0m Integrations enterprise test delete labels (0.09s)
1556:  �[32m✓�[0m Integrations enterprise test delete team certificate templates (2.30s)
1557:  �[32m✓�[0m Integrations enterprise test desktop endpoint with invalid policy (0.07s)
1558:  �[32m✓�[0m Integrations enterprise test device authentication methods (0.31s)
1559:  �[32m✓�[0m Integrations enterprise test device authentication methods cert serial zero should fail cert auth (0.00s)
1560:  �[32m✓�[0m Integrations enterprise test device authentication methods certificate for wrong host (0.00s)
...

1581:  �[32m✓�[0m Integrations enterprise test enqueue same script twice (0.09s)
1582:  �[32m✓�[0m Integrations enterprise test external integrations team config (0.29s)
1583:  �[32m✓�[0m Integrations enterprise test fleet desktop settings alternative browser host URL (0.08s)
1584:  �[32m✓�[0m Integrations enterprise test get user returns API endpoints (0.09s)
1585:  �[32m✓�[0m Integrations enterprise test git ops exceptions config (0.10s)
1586:  �[32m✓�[0m Integrations enterprise test git ops mode config (0.07s)
1587:  �[32m✓�[0m Integrations enterprise test git ops mode toggle does not create conditional access activity (0.13s)
1588:  �[32m✓�[0m Integrations enterprise test git ops user actions (1.24s)
1589:  �[32m✓�[0m Integrations enterprise test global policy create read patch (0.08s)
1590:  �[32m✓�[0m Integrations enterprise test host device mapping IDP (0.21s)
1591:  �[32m✓�[0m Integrations enterprise test host health (0.10s)
1592:  �[32m✓�[0m Integrations enterprise test host script details (0.32s)
1593:  �[32m✓�[0m Integrations enterprise test host script details deleted script (0.01s)
1594:  �[32m✓�[0m Integrations enterprise test host script details get script results user message (0.03s)
1595:  �[32m✓�[0m Integrations enterprise test host script details get script results user message disabled (0.00s)
1596:  �[32m✓�[0m Integrations enterprise test host script details get script results user message error (0.00s)
1597:  �[32m✓�[0m Integrations enterprise test host script details get script results user message host-timeout (0.00s)
...

1660:  �[32m✓�[0m Integrations enterprise test modify API only user premium assign to fleet clears global role (0.01s)
1661:  �[32m✓�[0m Integrations enterprise test modify API only user premium empty api endpoints (0.01s)
1662:  �[32m✓�[0m Integrations enterprise test modify API only user premium empty array is invalid (0.01s)
1663:  �[32m✓�[0m Integrations enterprise test modify API only user premium global role and fleets together (0.01s)
1664:  �[32m✓�[0m Integrations enterprise test modify API only user premium invalid api endpoint not in catalog (0.01s)
1665:  �[32m✓�[0m Integrations enterprise test modify API only user premium more than 100 api endpoints (0.01s)
1666:  �[32m✓�[0m Integrations enterprise test modify API only user premium non-a p i -only user (0.00s)
1667:  �[32m✓�[0m Integrations enterprise test modify API only user premium nonexistent user (0.00s)
1668:  �[32m✓�[0m Integrations enterprise test modify API only user premium null api endpoints resets to full access (0.01s)
1669:  �[32m✓�[0m Integrations enterprise test modify API only user premium update api endpoints to specific endpoints (0.01s)
1670:  �[32m✓�[0m Integrations enterprise test modify API only user premium update global role (0.01s)
1671:  �[32m✓�[0m Integrations enterprise test modify API only user premium update name (0.01s)
1672:  �[32m✓�[0m Integrations enterprise test modify API only user premium wildcard mixed with other entries (0.01s)
1673:  �[32m✓�[0m Integrations enterprise test modify team enroll secrets (0.11s)
1674:  �[32m✓�[0m Integrations enterprise test modify team historical data (0.14s)
1675:  �[32m✓�[0m Integrations enterprise test no team failing policy webhook trigger (0.10s)
1676:  �[32m✓�[0m Integrations enterprise test no team policies (0.28s)
...

1735:  �[32m✓�[0m Integrations enterprise test setup experience windows with software (0.51s)
1736:  �[32m✓�[0m Integrations enterprise test setup experience windows with software windows-failure-no-team (0.10s)
1737:  �[32m✓�[0m Integrations enterprise test setup experience windows with software windows-success (0.10s)
1738:  �[32m✓�[0m Integrations enterprise test setup experience windows with software without desktop (0.23s)
1739:  �[32m✓�[0m Integrations enterprise test software auth (1.10s)
1740:  �[32m✓�[0m Integrations enterprise test software auth global-admin (0.14s)
1741:  �[32m✓�[0m Integrations enterprise test software auth global-maintainer (0.15s)
1742:  �[32m✓�[0m Integrations enterprise test software auth global-observer (0.14s)
1743:  �[32m✓�[0m Integrations enterprise test software auth team-admin-belongs-to-team (0.07s)
1744:  �[32m✓�[0m Integrations enterprise test software auth team-admin-does-not-belong-to-team (0.07s)
1745:  �[32m✓�[0m Integrations enterprise test software auth team-maintainer-belongs-to-team (0.08s)
1746:  �[32m✓�[0m Integrations enterprise test software auth team-maintainer-does-not-belong-to-team (0.07s)
1747:  �[32m✓�[0m Integrations enterprise test software auth team-observer-belongs-to-team (0.07s)
1748:  �[32m✓�[0m Integrations enterprise test software auth team-observer-does-not-belong-to-team (0.06s)
1749:  �[32m✓�[0m Integrations enterprise test software installer host requests (3.27s)
1750:  �[32m✓�[0m Integrations enterprise test software installer host requests failed (0.03s)
1751:  �[32m✓�[0m Integrations enterprise test software installer host requests installed (0.03s)
1752:  �[32m✓�[0m Integrations enterprise test software installer host requests nonexistent title id failed (0.01s)
1753:  �[32m✓�[0m Integrations enterprise test software installer host requests nonexistent title id installed (0.01s)
1754:  �[32m✓�[0m Integrations enterprise test software installer host requests nonexistent title id pending (0.02s)
1755:  �[32m✓�[0m Integrations enterprise test software installer host requests pending (0.03s)
1756:  �[32m✓�[0m Integrations enterprise test software installer new install request platform validation (0.31s)
1757:  �[32m✓�[0m Integrations enterprise test software installer orbit download failure (0.17s)
1758:  �[32m✓�[0m Integrations enterprise test software installer upload download and delete (1.01s)
...

1779:  �[32m✓�[0m Integrations enterprise test team endpoints (0.44s)
1780:  �[32m✓�[0m Integrations enterprise test team labels (0.44s)
1781:  �[32m✓�[0m Integrations enterprise test team labels distributed read write (0.33s)
1782:  �[32m✓�[0m Integrations enterprise test team policies (0.27s)
1783:  �[32m✓�[0m Integrations enterprise test team policy continuous automations CRUD (0.14s)
1784:  �[32m✓�[0m Integrations enterprise test team policy create read patch (0.10s)
1785:  �[32m✓�[0m Integrations enterprise test team queries (0.09s)
1786:  �[32m✓�[0m Integrations enterprise test team schedule (0.11s)
1787:  �[32m✓�[0m Integrations enterprise test team secrets are obfuscated (0.78s)
1788:  �[32m✓�[0m Integrations enterprise test team specs (0.35s)
1789:  �[32m✓�[0m Integrations enterprise test team specs permissions (0.23s)
1790:  �[32m✓�[0m Integrations enterprise test update software auto update config (0.20s)
1791:  �[31m✖�[0m Integrations enterprise test upgrade codes from maintained apps (4.07s)
1792:  �[32m✓�[0m Integrations enterprise test windows migrate MDM not enabled (0.04s)
1793:  �[32m✓�[0m Integrations enterprise test windows updates team config (0.19s)
1794:  === �[31mFailed�[0m
1795:  === �[31mFAIL�[0m: server/service TestIntegrationsEnterprise/TestUpgradeCodesFromMaintainedApps (4.07s)
1796:  integration_enterprise_test.go:21484: 
1797:  Error Trace:	/home/runner/work/fleet/fleet/server/datastore/mysql/mysqltest/mysqltest.go:487
1798:  /home/runner/work/fleet/fleet/server/service/integration_enterprise_test.go:21484
1799:  /opt/hostedtoolcache/go/1.26.4/x64/src/runtime/asm_amd64.s:1771
1800:  Error:      	Received unexpected error:
1801:  sql: no rows in result set
1802:  Test:       	TestIntegrationsEnterprise/TestUpgradeCodesFromMaintainedApps
1803:  ts=level=debug msg="cleanup orphaned software titles" rows_affected=0 took=535.914µs
1804:  --- FAIL: TestIntegrationsEnterprise/TestUpgradeCodesFromMaintainedApps (4.07s)
1805:  === �[31mFAIL�[0m: server/service TestIntegrationsEnterprise (221.96s)
1806:  time=level=INFO msg="" method=POST uri=/api/latest/fleet/login took=83.395165ms op=login email=admin1@example.com public_ip=127.0.0.1
1807:  DONE 343 tests, 2 failures in 222.337s
1808:  make[1]: *** [Makefile:291: .run-go-tests] Error 1
1809:  make[1]: Leaving directory '/home/runner/work/fleet/fleet'
1810:  make: *** [Makefile:406: test-go] Error 2
1811:  ##[error]Process completed with exit code 2.
1812:  Node 20 is being deprecated. This workflow is running with Node 24 by default. If you need to temporarily use Node 20, you can set the ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION=true environment variable. For more information see: https://github.blog/changelog/2025-09-19-deprecation-of-node-20-on-github-actions-runners/
1813:  ##[group]Run actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a
1814:  with:
1815:  name: integration-enterprise-mysql8.0.44-coverage
1816:  path: ./coverage.txt
1817:  if-no-files-found: error
1818:  compression-level: 6
...

1821:  RACE_ENABLED: false
1822:  GO_TEST_TIMEOUT: 20m
1823:  DOCKER_COMMAND: docker compose -f docker-compose.yml -f docker-compose-redis-cluster.yml up -d mysql_test mysql_replica_test redis redis-cluster-1 redis-cluster-2 redis-cluster-3 redis-cluster-4 redis-cluster-5 redis-cluster-6 redis-cluster-setup s3 saml_idp mailhog mailpit smtp4dev_test
1824:  RUN_TESTS_ARG: -run=^TestIntegrationsEnterprise
1825:  CI_TEST_PKG: service
1826:  NEED_DOCKER: 1
1827:  ARTIFACT_PREFIX: integration-enterprise-mysql8.0.44
1828:  GOTOOLCHAIN: local
1829:  ##[endgroup]
1830:  (node:23476) [DEP0040] DeprecationWarning: The `punycode` module is deprecated. Please use a userland alternative instead.
1831:  (Use `node --trace-deprecation ...` to show where the warning was created)
1832:  With the provided path, there will be 1 file uploaded
1833:  Artifact name is valid!
1834:  Root directory input is valid!
1835:  Beginning upload of artifact content to blob storage
1836:  (node:23476) [DEP0169] DeprecationWarning: `url.parse()` behavior is not standardized and prone to errors that have security implications. Use the WHATWG URL API instead. CVEs are not issued for `url.parse()` vulnerabilities.
1837:  Uploaded bytes 561561
1838:  Finished uploading artifact content to blob storage!
1839:  SHA256 hash of uploaded artifact zip is 97ac1d3bfabf3ccdd3a56aca34ab8883dbe74bace9bc5bae5ec3fde549c6e31d
1840:  Finalizing artifact upload
1841:  Artifact integration-enterprise-mysql8.0.44-coverage.zip successfully finalized. Artifact ID 7757084713
1842:  Artifact integration-enterprise-mysql8.0.44-coverage has been successfully uploaded! Final size is 561561 bytes. Artifact ID is 7757084713
1843:  Artifact download URL: https://github.com/fleetdm/fleet/actions/runs/27844385263/artifacts/7757084713
1844:  ##[group]Run c1grep() { grep "$@" || test $? = 1; }
1845:  �[36;1mc1grep() { grep "$@" || test $? = 1; }�[0m
1846:  �[36;1mc1grep -oP 'FAIL: .*$' /tmp/gotest.log > /tmp/summary.txt�[0m
1847:  �[36;1mc1grep 'test timed out after' /tmp/gotest.log >> /tmp/summary.txt�[0m
1848:  �[36;1mc1grep 'fatal error:' /tmp/gotest.log >> /tmp/summary.txt�[0m
1849:  �[36;1mc1grep -A 10 'panic: runtime error: ' /tmp/gotest.log >> /tmp/summary.txt�[0m
1850:  �[36;1mc1grep ' FAIL\t' /tmp/gotest.log >> /tmp/summary.txt�[0m
1851:  �[36;1mGO_FAIL_SUMMARY=$(head -n 5 /tmp/summary.txt | sed ':a;N;$!ba;s/\n/\\n/g')�[0m
1852:  �[36;1mecho "GO_FAIL_SUMMARY=$GO_FAIL_SUMMARY"�[0m
1853:  �[36;1mif [[ -z "$GO_FAIL_SUMMARY" ]]; then�[0m
1854:  �[36;1m  GO_FAIL_SUMMARY="unknown, please check the build URL"�[0m
1855:  �[36;1mfi�[0m
1856:  �[36;1mGO_FAIL_SUMMARY=$GO_FAIL_SUMMARY envsubst < .github/workflows/config/slack_payload_template.json > ./payload.json�[0m
1857:  shell: /usr/bin/bash --noprofile --norc -e -o pipefail {0}
1858:  env:
1859:  RACE_ENABLED: false
1860:  GO_TEST_TIMEOUT: 20m
1861:  DOCKER_COMMAND: docker compose -f docker-compose.yml -f docker-compose-redis-cluster.yml up -d mysql_test mysql_replica_test redis redis-cluster-1 redis-cluster-2 redis-cluster-3 redis-cluster-4 redis-cluster-5 redis-cluster-6 redis-cluster-setup s3 saml_idp mailhog mailpit smtp4dev_test
1862:  RUN_TESTS_ARG: -run=^TestIntegrationsEnterprise
1863:  CI_TEST_PKG: service
1864:  NEED_DOCKER: 1
1865:  ARTIFACT_PREFIX: integration-enterprise-mysql8.0.44
1866:  GOTOOLCHAIN: local
1867:  ##[endgroup]
1868:  GO_FAIL_SUMMARY=FAIL: TestIntegrationsEnterprise/TestUpgradeCodesFromMaintainedApps (4.07s)
1869:  Node 20 is being deprecated. This workflow is running with Node 24 by default. If you need to temporarily use Node 20, you can set the ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION=true environment variable. For more information see: https://github.blog/changelog/2025-09-19-deprecation-of-node-20-on-github-actions-runners/
1870:  ##[group]Run actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a
1871:  with:
1872:  name: integration-enterprise-mysql8.0.44-test-log
1873:  path: /tmp/gotest.log
1874:  if-no-files-found: error
1875:  compression-level: 6
...

1878:  RACE_ENABLED: false
1879:  GO_TEST_TIMEOUT: 20m
1880:  DOCKER_COMMAND: docker compose -f docker-compose.yml -f docker-compose-redis-cluster.yml up -d mysql_test mysql_replica_test redis redis-cluster-1 redis-cluster-2 redis-cluster-3 redis-cluster-4 redis-cluster-5 redis-cluster-6 redis-cluster-setup s3 saml_idp mailhog mailpit smtp4dev_test
1881:  RUN_TESTS_ARG: -run=^TestIntegrationsEnterprise
1882:  CI_TEST_PKG: service
1883:  NEED_DOCKER: 1
1884:  ARTIFACT_PREFIX: integration-enterprise-mysql8.0.44
1885:  GOTOOLCHAIN: local
1886:  ##[endgroup]
1887:  (node:23499) [DEP0040] DeprecationWarning: The `punycode` module is deprecated. Please use a userland alternative instead.
1888:  (Use `node --trace-deprecation ...` to show where the warning was created)
1889:  With the provided path, there will be 1 file uploaded
1890:  Artifact name is valid!
1891:  Root directory input is valid!
1892:  Beginning upload of artifact content to blob storage
1893:  (node:23499) [DEP0169] DeprecationWarning: `url.parse()` behavior is not standardized and prone to errors that have security implications. Use the WHATWG URL API instead. CVEs are not issued for `url.parse()` vulnerabilities.
1894:  Uploaded bytes 6580
...

1910:  RACE_ENABLED: false
1911:  GO_TEST_TIMEOUT: 20m
1912:  DOCKER_COMMAND: docker compose -f docker-compose.yml -f docker-compose-redis-cluster.yml up -d mysql_test mysql_replica_test redis redis-cluster-1 redis-cluster-2 redis-cluster-3 redis-cluster-4 redis-cluster-5 redis-cluster-6 redis-cluster-setup s3 saml_idp mailhog mailpit smtp4dev_test
1913:  RUN_TESTS_ARG: -run=^TestIntegrationsEnterprise
1914:  CI_TEST_PKG: service
1915:  NEED_DOCKER: 1
1916:  ARTIFACT_PREFIX: integration-enterprise-mysql8.0.44
1917:  GOTOOLCHAIN: local
1918:  ##[endgroup]
1919:  (node:23513) [DEP0040] DeprecationWarning: The `punycode` module is deprecated. Please use a userland alternative instead.
1920:  (Use `node --trace-deprecation ...` to show where the warning was created)
1921:  With the provided path, there will be 1 file uploaded
1922:  Artifact name is valid!
1923:  Root directory input is valid!
1924:  Beginning upload of artifact content to blob storage
1925:  (node:23513) [DEP0169] DeprecationWarning: `url.parse()` behavior is not standardized and prone to errors that have security implications. Use the WHATWG URL API instead. CVEs are not issued for `url.parse()` vulnerabilities.
1926:  Uploaded bytes 212
...

1942:  RACE_ENABLED: false
1943:  GO_TEST_TIMEOUT: 20m
1944:  DOCKER_COMMAND: docker compose -f docker-compose.yml -f docker-compose-redis-cluster.yml up -d mysql_test mysql_replica_test redis redis-cluster-1 redis-cluster-2 redis-cluster-3 redis-cluster-4 redis-cluster-5 redis-cluster-6 redis-cluster-setup s3 saml_idp mailhog mailpit smtp4dev_test
1945:  RUN_TESTS_ARG: -run=^TestIntegrationsEnterprise
1946:  CI_TEST_PKG: service
1947:  NEED_DOCKER: 1
1948:  ARTIFACT_PREFIX: integration-enterprise-mysql8.0.44
1949:  GOTOOLCHAIN: local
1950:  ##[endgroup]
1951:  (node:23547) [DEP0040] DeprecationWarning: The `punycode` module is deprecated. Please use a userland alternative instead.
1952:  (Use `node --trace-deprecation ...` to show where the warning was created)
1953:  With the provided path, there will be 1 file uploaded
1954:  Artifact name is valid!
1955:  Root directory input is valid!
1956:  Beginning upload of artifact content to blob storage
1957:  (node:23547) [DEP0169] DeprecationWarning: `url.parse()` behavior is not standardized and prone to errors that have security implications. Use the WHATWG URL API instead. CVEs are not issued for `url.parse()` vulnerabilities.
1958:  Uploaded bytes 119074
...

1991:  RACE_ENABLED: false
1992:  GO_TEST_TIMEOUT: 20m
1993:  DOCKER_COMMAND: docker compose -f docker-compose.yml -f docker-compose-redis-cluster.yml up -d mysql_test mysql_replica_test redis redis-cluster-1 redis-cluster-2 redis-cluster-3 redis-cluster-4 redis-cluster-5 redis-cluster-6 redis-cluster-setup s3 saml_idp mailhog mailpit smtp4dev_test
1994:  RUN_TESTS_ARG: -run=^TestIntegrationsEnterprise
1995:  CI_TEST_PKG: service
1996:  NEED_DOCKER: 1
1997:  ARTIFACT_PREFIX: integration-enterprise-mysql8.0.44
1998:  GOTOOLCHAIN: local
1999:  ##[endgroup]
2000:  (node:23560) [DEP0040] DeprecationWarning: The `punycode` module is deprecated. Please use a userland alternative instead.
2001:  (Use `node --trace-deprecation ...` to show where the warning was created)
2002:  With the provided path, there will be 1 file uploaded
2003:  Artifact name is valid!
2004:  Root directory input is valid!
2005:  Beginning upload of artifact content to blob storage
2006:  (node:23560) [DEP0169] DeprecationWarning: `url.parse()` behavior is not standardized and prone to errors that have security implications. Use the WHATWG URL API instead. CVEs are not issued for `url.parse()` vulnerabilities.
2007:  Uploaded bytes 133

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants