Skip to content

fix: encode secret keys to support '/' in paths#5614

Open
vinodwagh07 wants to merge 2 commits intoInfisical:mainfrom
vinodwagh07:fix/secrets/ENG-4643-support-slash-path-encoding
Open

fix: encode secret keys to support '/' in paths#5614
vinodwagh07 wants to merge 2 commits intoInfisical:mainfrom
vinodwagh07:fix/secrets/ENG-4643-support-slash-path-encoding

Conversation

@vinodwagh07
Copy link

Description

This PR addresses issue #5598 where secret keys containing forward slashes (/) caused 404 errors because the character was interpreted as a URL path separator.

Changes

  • Applied encodeURIComponent to secret keys in frontend queries and mutations to ensure they are passed as a single string (e.g., test%2Fcred).
  • Updated E2E test helpers to validate keys with special characters.
  • Validated compatibility with AWS Parameter Store naming conventions.

Related Issues

Fixes #5598

@maidul98
Copy link
Collaborator

maidul98 commented Mar 6, 2026

Snyk checks have passed. No issues have been found so far.

Status Scanner Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Mar 6, 2026

Greptile Summary

This PR enables forward slashes (/) in secret keys by switching to wildcard routes on the backend and applying encodeURIComponent on the frontend. The frontend changes are sound, but three critical backend issues prevent the feature from working end-to-end.

Key issues found:

  1. Syntax error (lines 1311 & 1367): The reference-tree handlers attempt const { secretName } = req.params["*"], but req.params["*"] is a string, not an object. Destructuring fails silently, leaving secretName = undefined and breaking both reference-dependency-tree and secret-reference-tree endpoints.

  2. Missing URL decoding: req.params["*"] contains the percent-encoded form (e.g., path%2Fto%2Fsecret) because Fastify's router intentionally does not decode %2F to prevent path traversal. Without calling decodeURIComponent, the backend looks up secrets under the encoded key name rather than the original key with actual slashes. This breaks the core use case.

  3. Test coverage gap: The new test cases pass pre-encoded strings into the helper, creating secrets with literal percent-encoded key names rather than keys with actual slashes. The round-trip test passes, but the actual feature (slash-in-key support) is never verified.

The first two issues will cause runtime failures; the third masks these issues in the test suite.

Confidence Score: 1/5

  • Not safe to merge — two critical bugs prevent the feature from functioning. Destructuring error will cause runtime failures in reference-tree endpoints; missing URL decoding breaks secret lookup for keys with slashes.
  • The PR introduces two blocking issues in the backend routes: (1) a syntax error at lines 1311 and 1367 where const { secretName } = req.params["*"] attempts to destructure from a string, leaving secretName undefined; (2) missing decodeURIComponent calls that prevent percent-encoded keys from matching stored secrets. These bugs cause both runtime failures and silent data mismatches. The test suite does not catch these issues because test data pre-encodes keys, masking the actual feature requirements.
  • backend/src/server/routes/v4/secret-router.ts (destructuring error + missing URL decode), backend/e2e-test/routes/v4/secrets.spec.ts (test data does not exercise actual use case)

Comments Outside Diff (1)

  1. backend/src/server/routes/v4/secret-router.ts, line 339-343 (link)

    Missing decodeURIComponent on wildcard param

    When the frontend sends an encoded secret key like path%2Fto%2Fsecret in the URL (from encodeURIComponent("path/to/secret")), Fastify's router intentionally does not decode %2F to the / character — doing so would allow path traversal attacks. As a result, req.params["*"] contains the still-encoded string "path%2Fto%2Fsecret".

    The code then passes this encoded value directly to the service layer (line 339):

    secretName: req.params["*"],

    This causes the backend to look up the secret under the literal key path%2Fto%2Fsecret instead of the original path/to/secret. Any secret imported or created with an actual forward slash in its key will not be found.

    Add decodeURIComponent to all handlers that use req.params["*"] before passing to service/database layers:

    This applies to the GET, POST, PATCH, and DELETE handlers, as well as the two reference-tree handlers.

Last reviewed commit: 31b540f

tagIds
}) => {
const { data } = await apiRequest.post(`/api/v4/secrets/${secretKey}`, {
const { data } = await apiRequest.post(`/api/v4/secrets/${encodeURIComponent(secretKey)}`, {
Copy link
Contributor

Choose a reason for hiding this comment

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

POST endpoint will still reject secret names containing / after encoding

The backend v4 POST route at /:secretName uses SecretNameSchema (in backend/src/server/lib/schemas.ts:45-48) which explicitly rejects names containing /:

export const SecretNameSchema = BaseSecretNameSchema.refine(
  (el) => !el.includes(":") && !el.includes("/"),
  "Secret name cannot contain colon or forward slash."
);

The frontend correctly encodes the secret key as %2F before sending the request, but Fastify's router URL-decodes the path parameter before schema validation. So %2F becomes / and fails the SecretNameSchema check with a 400 error.

Impact: The fix works for GET, PATCH, and DELETE operations (which use permissive validators like z.string().trim() or BaseSecretNameSchema), but creating a new secret with / in the key will still fail on the backend. This PR fixes retrieval/update/deletion for secrets that already have / in their keys (e.g., synced from AWS Parameter Store), but not new creation.

To support creation: The POST route's SecretNameSchema validation should be updated to permit / in the key, or Fastify/find-my-way should be configured to skip decoding %2F for path parameters before validation.

const getSecretValue = async (secretKey: string) => {
const passwordSecret = await testServer.inject({
url: `/api/v3/secrets/raw/${secretKey}`,
url: `/api/v3/secrets/raw/${encodeURIComponent(secretKey)}`,
Copy link
Contributor

Choose a reason for hiding this comment

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

Test coverage gap: Secret keys with / are never exercised

The encodeURIComponent addition is correct and defensive, but the test cases do not actually validate keys containing /. All secret names are generated via formatSqlUsername() (e.g., MYSQL_PASSWORD_ABCD1234), which only produces alphanumeric characters and underscores.

To verify the fix works for its intended purpose, add a dedicated test case where a secret key contains / (e.g., by passing a static key like "aws/parameter/store" instead of a formatted username) and assert that the secret retrieval succeeds end-to-end.

This ensures that secret names containing '/' are treated as part of the key name rather than URL path separators.

- Encodes keys in frontend mutations and queries to prevent 404 errors.
- Updates E2E tests to match encoded API behavior.

Fixes Infisical#5598
@vinodwagh07 vinodwagh07 force-pushed the fix/secrets/ENG-4643-support-slash-path-encoding branch from 21a67bc to 73fa07e Compare March 6, 2026 15:24
@vinodwagh07
Copy link
Author

@greptile-apps I have addressed the feedback. I updated the SecretNameSchema in backend/src/server/lib/schemas.ts to allow forward slashes and added dedicated E2E test cases in secretTestCases that explicitly use keys with slashes. Please re-run the analysis.

Comment on lines +1311 to 1312
const { secretName } = req.params["*"],
const { secretPath, environment, projectId } = req.query;
Copy link
Contributor

Choose a reason for hiding this comment

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

Syntax error: destructuring from a string always yields undefined

req.params["*"] is a string value (e.g., "path%2Fto%2Fsecret"), not an object with a secretName property. Attempting to destructure const { secretName } = req.params["*"] will always leave secretName as undefined, breaking the getSecretReferenceDependencyTree call that follows at line 1314.

The same error occurs at line 1367 in the secret-reference-tree handler.

Suggested change
const { secretName } = req.params["*"],
const { secretPath, environment, projectId } = req.query;
const secretName = req.params["*"];
const { secretPath, environment, projectId } = req.query;

Comment on lines +162 to +165
key: encodeURIComponent("path/to/secret"),
value: "slash-value",
comment: "Testing forward slashes in keys"
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Test encodes the key at parse time, creating a literal percent-encoded string

This test stores encodeURIComponent("path/to/secret"), which evaluates to the literal string "path%2Fto%2Fsecret" at test definition time. The test then passes this already-encoded string into the URL without further encoding. The round-trip succeeds because both create and retrieve operations use the same encoded key.

However, the actual use case — a secret key containing a real forward slash character — is never tested. To verify the feature works, the test data should use the raw slash-containing key, and the createSecret helper should apply URL encoding when constructing the endpoint URL.

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.

Secret key names containing / fail to create - character interpreted as URL path separator

2 participants