fix(hooks): resolve cubic review issues
- Replace two-pass env interpolation with single-pass combined regex to prevent re-interpolation of $-sequences in substituted header values - Convert HookEntry to discriminated union so type: "http" requires url, preventing invalid configs from passing type checking - Add regression test for double-interpolation edge case
This commit is contained in:
parent
43dfdb2380
commit
3eb53adfc3
@ -80,16 +80,11 @@ export interface PluginManifest {
|
|||||||
/**
|
/**
|
||||||
* Hooks configuration
|
* Hooks configuration
|
||||||
*/
|
*/
|
||||||
export interface HookEntry {
|
export type HookEntry =
|
||||||
type: "command" | "prompt" | "agent" | "http"
|
| { type: "command"; command?: string }
|
||||||
command?: string
|
| { type: "prompt"; prompt?: string }
|
||||||
prompt?: string
|
| { type: "agent"; agent?: string }
|
||||||
agent?: string
|
| { type: "http"; url: string; headers?: Record<string, string>; allowedEnvVars?: string[]; timeout?: number }
|
||||||
url?: string
|
|
||||||
headers?: Record<string, string>
|
|
||||||
allowedEnvVars?: string[]
|
|
||||||
timeout?: number
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface HookMatcher {
|
export interface HookMatcher {
|
||||||
matcher?: string
|
matcher?: string
|
||||||
|
|||||||
@ -227,6 +227,15 @@ describe("interpolateEnvVars", () => {
|
|||||||
expect(result).toBe("abc:")
|
expect(result).toBe("abc:")
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("#given ${VAR} where value contains $ANOTHER #when both allowed #then does not double-interpolate", async () => {
|
||||||
|
process.env = { ...process.env, TOKEN: "val$SECRET", SECRET: "oops" }
|
||||||
|
const { interpolateEnvVars } = await import("./execute-http-hook")
|
||||||
|
|
||||||
|
const result = interpolateEnvVars("Bearer ${TOKEN}", ["TOKEN", "SECRET"])
|
||||||
|
|
||||||
|
expect(result).toBe("Bearer val$SECRET")
|
||||||
|
})
|
||||||
|
|
||||||
it("#given no allowedEnvVars #when called #then replaces all with empty", async () => {
|
it("#given no allowedEnvVars #when called #then replaces all with empty", async () => {
|
||||||
const { interpolateEnvVars } = await import("./execute-http-hook")
|
const { interpolateEnvVars } = await import("./execute-http-hook")
|
||||||
|
|
||||||
|
|||||||
@ -9,23 +9,14 @@ export function interpolateEnvVars(
|
|||||||
): string {
|
): string {
|
||||||
const allowedSet = new Set(allowedEnvVars)
|
const allowedSet = new Set(allowedEnvVars)
|
||||||
|
|
||||||
let result = value.replace(/\$\{(\w+)\}/g, (_match, varName: string) => {
|
return value.replace(/\$\{(\w+)\}|\$(\w+)/g, (_match, bracedVar: string | undefined, bareVar: string | undefined) => {
|
||||||
|
const varName = (bracedVar ?? bareVar) as string
|
||||||
if (allowedSet.has(varName)) {
|
if (allowedSet.has(varName)) {
|
||||||
return process.env[varName] ?? ""
|
return process.env[varName] ?? ""
|
||||||
}
|
}
|
||||||
return ""
|
return ""
|
||||||
})
|
})
|
||||||
|
|
||||||
result = result.replace(/\$(\w+)/g, (_match, varName: string) => {
|
|
||||||
if (allowedSet.has(varName)) {
|
|
||||||
return process.env[varName] ?? ""
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
})
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function resolveHeaders(
|
function resolveHeaders(
|
||||||
hook: HookHttp
|
hook: HookHttp
|
||||||
): Record<string, string> {
|
): Record<string, string> {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user