---
title: "GHSA-4xm8-rg9h-jhq2: Signal K resources API path traversal can disclose the JWT signing secret and enable remote code execution"
url: https://erroneo.us/security/ghsa/GHSA-4xm8-rg9h-jhq2/
description: "GHSA-4xm8-rg9h-jhq2 (critical, npm): Signal K resources API path traversal can disclose the JWT signing secret and enable remote code execution. Summary…"
type: advisory
section: "Security Digest"
date_published: 2026-10-30
date_modified: 2026-10-30
publisher: erroneo.us
author: erroneo.us Security Desk
ghsa: "GHSA-4xm8-rg9h-jhq2"
license: All rights reserved. Quote with attribution and a link.
---

# GHSA-4xm8-rg9h-jhq2: Signal K resources API path traversal can disclose the JWT signing secret and enable remote code execution

## Affected packages

- npm `signalk-server` >= 2.0.0, < 2.34.0 → fixed: 2.34.0
- npm `@signalk/resources-provider` >= 1.0.0, < 1.5.3 → fixed: 1.5.3

## Details

Summary Path traversal in the Signal K resources and applicationData APIs lets an unauthenticated or low-privileged user read, write and delete files outside the storage root. Reading /security.json discloses the JWT signing secret, enabling admin token forgery and remote code execution. Details Description The v2 REST resources API GET /signalk/v2/api/resources/:resourceType/:resourceId passes resourceId to FileStore.getResource() without validation. getResource() builds the file path with path.join(this.resources[type].path, itemUuid) followed by readFile + JSON.parse, so a %2F-encoded multi-level traversal (..%2F..%2F..%2F..%2F) escapes the resource storage directory and returns any JSON file on the server (source: src/api/resources/index.ts:559-600, packages/resources-provider-plugin/src/filestorage.ts:115-125). Because the JWT signing secret is stored in plaintext in /security.json (generated at src/tokensecurity.ts:271, persisted via saveSecurityConfig at src/security.ts:377), an unauthenticated attacker on deployments with allowreadonly=true (a supported, UI-toggleable configuration granting anonymous read access) can: 1. Read /security.json — disclosing the JWT signing secret (512-hex) and user password hashes. 2. Forge an admin JWT — HS256-sign {"id":"admin"} with the disclosed secret; accepted by /skServer/loginStatus (userLevel: "admin") and all admin-protected endpoints (adminAuthenticationMiddleware, src/tokensecurity.ts:593, applied at src/tokensecurity.ts:953). 3. Gain remote code execution — POST /skServer/providers (admin) accepts a provider of type Execute; packages/streams/src/execute.ts:93 executes it via spawn('sh', ['-c', command]) at creation time. The provider persists to settings.json and survives restarts. On default deployments (allowreadonly=false), step 1 returns 401 to anonymous users but succeeds with any readonly user token. Suggested fixes 1. Validate resourceId against a strict whitelist (for example, UUID format) in src/api/resources/index.ts before any storage call, including PUT/DELETE. 2. In FileStore.getResource/setResource/deleteResource, canonicalise with path.resolve() and reject results outside this.resources[type].path. 3. Store the JWT signing secret outside the API-readable config…

## Sources

- [GitHub Advisory GHSA-4xm8-rg9h-jhq2](https://github.com/advisories/GHSA-4xm8-rg9h-jhq2)
- [github.com](https://github.com/SignalK/signalk-server/security/advisories/GHSA-4xm8-rg9h-jhq2)
- [github.com](https://github.com/SignalK/signalk-server/commit/83689f040d7e90280dddc643f44f3e4ce348fbb1)
- [github.com](https://github.com/SignalK/signalk-server/releases/tag/v2.34.0)

---
Source page: https://erroneo.us/security/ghsa/GHSA-4xm8-rg9h-jhq2/ · Security Digest: https://erroneo.us/security/ · More: https://erroneo.us/llms.txt
