| author | |
| committer | |
| log | 5e5abc92f472d764f232316d41f33e9159b81056 |
| tree | 3c305ad14bd740ece86d52909d60263648bc4f68 |
| parent | 5e5c52b7a70c16734e42faa072248a597e1333ee |
| signature | Signed by PGP key B5690EEEBB952194 |
Co-authored-by: Simone <185146821+Lucenx9@users.noreply.github.com>2 files changed, 23 insertions(+), 2 deletions(-)
apps/server/src/scheduledTasks/webhookTemplate.test.ts+19| ... | @@ -52,6 +52,25 @@ describe("renderWebhookPrompt", () => { | ... | @@ -52,6 +52,25 @@ describe("renderWebhookPrompt", () => { |
| 52 | assert.deepEqual(rendered.missing, ["body.nope", "headers.x-missing", "unknown"]); | 52 | assert.deepEqual(rendered.missing, ["body.nope", "headers.x-missing", "unknown"]); |
| 53 | }); | 53 | }); |
| 54 | 54 | ||
| 55 | it("trims placeholder whitespace before resolving and reporting missing fields", () => { | ||
| 56 | const rendered = renderWebhookPrompt( | ||
| 57 | "{{\tbody.action\n}}|{{ body.nope }}|{{\nbody.nope\t}}|{{ \t\n }}", | ||
| 58 | githubPullRequest, | ||
| 59 | ); | ||
| 60 | assert.equal(rendered.prompt, "opened|||"); | ||
| 61 | assert.deepEqual(rendered.missing, ["body.nope", ""]); | ||
| 62 | }); | ||
| 63 | |||
| 64 | it("preserves unclosed whitespace-heavy placeholders and resolves later valid ones", () => { | ||
| 65 | const unfinished = `Notify {{${" ".repeat(1024)}body`; | ||
| 66 | const rendered = renderWebhookPrompt( | ||
| 67 | `${unfinished}\n{{body.pull_request.number}}`, | ||
| 68 | githubPullRequest, | ||
| 69 | ); | ||
| 70 | assert.equal(rendered.prompt, `${unfinished}\n45`); | ||
| 71 | assert.deepEqual(rendered.missing, []); | ||
| 72 | }); | ||
| 73 | |||
| 55 | it("addresses form-encoded bodies and leaves other bodies to {{body}}", () => { | 74 | it("addresses form-encoded bodies and leaves other bodies to {{body}}", () => { |
| 56 | const form = renderWebhookPrompt("{{body.text}} by {{body.user_name}}", { | 75 | const form = renderWebhookPrompt("{{body.text}} by {{body.user_name}}", { |
| 57 | ...githubPullRequest, | 76 | ...githubPullRequest, |
apps/server/src/scheduledTasks/webhookTemplate.ts+4-2| ... | @@ -69,7 +69,8 @@ export function redactQuery(query: string): string { | ... | @@ -69,7 +69,8 @@ export function redactQuery(query: string): string { |
| 69 | .join("&"); | 69 | .join("&"); |
| 70 | } | 70 | } |
| 71 | 71 | ||
| 72 | const PLACEHOLDER = /\{\{\s*([^{}]*?)\s*\}\}/g; | 72 | // Trim after matching so unclosed placeholders cannot backtrack over whitespace partitions. |
| 73 | const PLACEHOLDER = /\{\{([^{}]*)\}\}/g; | ||
| 73 | 74 | ||
| 74 | function parseBody(request: WebhookRequest): unknown { | 75 | function parseBody(request: WebhookRequest): unknown { |
| 75 | const contentType = (request.headers["content-type"] ?? "").toLowerCase(); | 76 | const contentType = (request.headers["content-type"] ?? "").toLowerCase(); |
| ... | @@ -144,7 +145,8 @@ export function renderWebhookPrompt( | ... | @@ -144,7 +145,8 @@ export function renderWebhookPrompt( |
| 144 | } | 145 | } |
| 145 | }; | 146 | }; |
| 146 | 147 | ||
| 147 | const prompt = template.replace(PLACEHOLDER, (_match, expression: string) => { | 148 | const prompt = template.replace(PLACEHOLDER, (_match, rawExpression: string) => { |
| 149 | const expression = rawExpression.trim(); | ||
| 148 | const value = resolve(expression); | 150 | const value = resolve(expression); |
| 149 | if (value === undefined || value === null) { | 151 | if (value === undefined || value === null) { |
| 150 | if (!missing.includes(expression)) missing.push(expression); | 152 | if (!missing.includes(expression)) missing.push(expression); |