fix(common): correctly resolve secret environment variables in basic auth header (#5879)
Co-authored-by: James George <25279263+jamesgeorge007@users.noreply.github.com>
This commit is contained in:
parent
2ad2f46e6a
commit
98aa0368fb
3 changed files with 45 additions and 16 deletions
|
|
@ -131,7 +131,10 @@ import {
|
||||||
getEffectiveRESTRequest,
|
getEffectiveRESTRequest,
|
||||||
resolvesEnvsInBody,
|
resolvesEnvsInBody,
|
||||||
} from "~/helpers/utils/EffectiveURL"
|
} from "~/helpers/utils/EffectiveURL"
|
||||||
import { AggregateEnvironment, getAggregateEnvs } from "~/newstore/environments"
|
import {
|
||||||
|
AggregateEnvironment,
|
||||||
|
getAggregateEnvsWithCurrentValue,
|
||||||
|
} from "~/newstore/environments"
|
||||||
|
|
||||||
import { useService } from "dioc/vue"
|
import { useService } from "dioc/vue"
|
||||||
import cloneDeep from "lodash-es/cloneDeep"
|
import cloneDeep from "lodash-es/cloneDeep"
|
||||||
|
|
@ -237,7 +240,7 @@ const getFinalURL = (input: string): string => {
|
||||||
* Combines all environment variables into a single environment object
|
* Combines all environment variables into a single environment object
|
||||||
*/
|
*/
|
||||||
const buildFinalEnvironment = (): Environment => {
|
const buildFinalEnvironment = (): Environment => {
|
||||||
const aggregateEnvs = getAggregateEnvs()
|
const aggregateEnvs = getAggregateEnvsWithCurrentValue()
|
||||||
const inheritedVariables =
|
const inheritedVariables =
|
||||||
currentActiveTabDocument.value.inheritedProperties?.variables || []
|
currentActiveTabDocument.value.inheritedProperties?.variables || []
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -49,5 +49,25 @@ describe("Basic Auth", () => {
|
||||||
|
|
||||||
expect(headers[0].value).toBe(`Basic ${btoa(":")}`)
|
expect(headers[0].value).toBe(`Basic ${btoa(":")}`)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test("resolves secret environment variables before base64 encoding even when `showKeyIfSecret` is `true`", async () => {
|
||||||
|
const auth: HoppRESTAuth & { authType: "basic" } = {
|
||||||
|
authActive: true,
|
||||||
|
authType: "basic",
|
||||||
|
username: "<<USERNAME>>",
|
||||||
|
password: "<<PASSWORD>>",
|
||||||
|
}
|
||||||
|
|
||||||
|
// `showKeyIfSecret = true` should NOT affect base64 encoding
|
||||||
|
// Previously, this would encode "testuser:<<PASSWORD>>" instead of "testuser:testpass"
|
||||||
|
// See: https://github.com/hoppscotch/hoppscotch/issues/5863
|
||||||
|
const headers = await generateBasicAuthHeaders(
|
||||||
|
auth,
|
||||||
|
mockEnvVars,
|
||||||
|
true // showKeyIfSecret
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(headers[0].value).toBe(`Basic ${btoa("testuser:testpass")}`)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -5,29 +5,35 @@ import {
|
||||||
HoppRESTHeader,
|
HoppRESTHeader,
|
||||||
} from "@hoppscotch/data"
|
} from "@hoppscotch/data"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UTF-8 safe base64 encoding. Standard btoa() throws on non-ASCII chars,
|
||||||
|
* so we encode through TextEncoder first.
|
||||||
|
*/
|
||||||
|
function utf8Btoa(str: string): string {
|
||||||
|
const bytes = new TextEncoder().encode(str)
|
||||||
|
let binary = ""
|
||||||
|
for (const byte of bytes) {
|
||||||
|
binary += String.fromCharCode(byte)
|
||||||
|
}
|
||||||
|
return btoa(binary)
|
||||||
|
}
|
||||||
|
|
||||||
export async function generateBasicAuthHeaders(
|
export async function generateBasicAuthHeaders(
|
||||||
auth: HoppRESTAuth & { authType: "basic" },
|
auth: HoppRESTAuth & { authType: "basic" },
|
||||||
envVars: Environment["variables"],
|
envVars: Environment["variables"],
|
||||||
showKeyIfSecret = false
|
// `showKeyIfSecret` is intentionally not forwarded to `parseTemplateString()` here.
|
||||||
|
// The base64 encoding must always use actual values, otherwise the
|
||||||
|
// Authorization header is unusable (see #5863).
|
||||||
|
_showKeyIfSecret = false
|
||||||
): Promise<HoppRESTHeader[]> {
|
): Promise<HoppRESTHeader[]> {
|
||||||
const username = parseTemplateString(
|
const username = parseTemplateString(auth.username, envVars, false, false)
|
||||||
auth.username,
|
const password = parseTemplateString(auth.password, envVars, false, false)
|
||||||
envVars,
|
|
||||||
false,
|
|
||||||
showKeyIfSecret
|
|
||||||
)
|
|
||||||
const password = parseTemplateString(
|
|
||||||
auth.password,
|
|
||||||
envVars,
|
|
||||||
false,
|
|
||||||
showKeyIfSecret
|
|
||||||
)
|
|
||||||
|
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
active: true,
|
active: true,
|
||||||
key: "Authorization",
|
key: "Authorization",
|
||||||
value: `Basic ${btoa(`${username}:${password}`)}`,
|
value: `Basic ${utf8Btoa(`${username}:${password}`)}`,
|
||||||
description: "",
|
description: "",
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue