Skip to content

fix: use html/template to resolve unsafe-quoting alert in bootstrap page renderer#47333

Merged
pelikhan merged 3 commits into
mainfrom
copilot/fix-code-scanning-alert-650
Jul 22, 2026
Merged

fix: use html/template to resolve unsafe-quoting alert in bootstrap page renderer#47333
pelikhan merged 3 commits into
mainfrom
copilot/fix-code-scanning-alert-650

Conversation

Copilot AI commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

CodeQL alert #650 (go/unsafe-quoting, critical) flagged renderBootstrapGitHubAppRegistrationPage for embedding JSON manifest data — which inherently contains double quotes — into a double-quoted HTML attribute via manual string concatenation.

Changes

  • pkg/cli/bootstrap_profile_helpers.go: Replaces the one-liner manual string concatenation with html/template, using a package-level template constant and bytes.Buffer execution.
// Before: manual concatenation with htmlEscape wrapper
return "...value=\"" + htmlEscape(string(encoded)) + "\"...", nil

// After: context-aware template escaping
const bootstrapRegistrationPageTmpl = `...value="{{.Manifest}}"...`
t, _ := template.New("bootstrap").Parse(bootstrapRegistrationPageTmpl)
t.Execute(&buf, data{Action: registrationURL, Manifest: string(encoded)})

html/template is context-aware: it applies HTML attribute escaping to {{.Manifest}} and URL normalization + HTML escaping to {{.Action}}, using a well-known safe API that static analysis tools can reason about correctly.

…HubAppRegistrationPage (CodeQL alert #650)

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix code scanning alert #650 fix: use html/template to resolve unsafe-quoting alert in bootstrap page renderer Jul 22, 2026
Copilot AI requested a review from pelikhan July 22, 2026 14:43
@pelikhan
pelikhan marked this pull request as ready for review July 22, 2026 15:16
Copilot AI review requested due to automatic review settings July 22, 2026 15:16

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Replaces manual HTML construction with context-aware html/template rendering to address unsafe quoting.

Changes:

  • Adds a reusable bootstrap page template.
  • Escapes form action and manifest data by context.
  • Propagates template parsing and execution errors.
Show a summary per file
File Description
pkg/cli/bootstrap_profile_helpers.go Safely renders the GitHub App registration page.

Review details

Tip

Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

  • Files reviewed: 1/1 changed files
  • Comments generated: 0
  • Review effort level: Medium

@github-actions

github-actions Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

🧠 Matt Pocock Skills Reviewer has completed the skills-based review. ✅

@github-actions

github-actions Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

PR Code Quality Reviewer completed the code quality review.

@github-actions

github-actions Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Design Decision Gate 🏗️ completed the design decision gate check.

No ADR enforcement needed: PR does not have the 'implementation' label and has ≤100 new lines of code in business logic directories (20 additions detected, threshold is 100).

@github-actions

github-actions Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Test Quality Sentinel completed test quality analysis.

No test files were added or modified in this PR. This change modifies only pkg/cli/bootstrap_profile_helpers.go (production code). Test Quality Sentinel analysis skipped.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Review: html/template migration

The change correctly resolves the CodeQL go/unsafe-quoting alert by switching from manual string concatenation to html/template, which applies context-aware escaping automatically. Implementation is clean and handles errors at both Parse and Execute steps.

Minor (non-blocking): The template is parsed on every call to renderBootstrapGitHubAppRegistrationPage. Since it's a package-level constant, consider pre-parsing once:

var bootstrapRegistrationPage = template.Must(template.New("bootstrap").Parse(bootstrapRegistrationPageTmpl))

This avoids repeated parse overhead, though the call is infrequent so impact is negligible.

LGTM.

🧵 Reviewed using Impeccable skills by Impeccable Skills Reviewer · sonnet46 15.5 AIC · ⌖ 5.69 AIC · ⊞ 5K

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Skills-Based Review 🧠

Applied /diagnosing-bugs and /tdd — no blocking issues, but two improvements are worth addressing before merge.

📋 Key Themes & Highlights

Issues Found

  1. Template re-parsed on every call (line 257) — template.New(...).Parse(...) runs on every invocation. Move to a package-level template.Must var for correctness (panics at startup if invalid) and efficiency.
  2. Missing URL edge-case test (line 262) — html/template sanitises the action attribute as a URL context; a URL rejected by the sanitiser silently becomes #ZgotmplZ. The existing test doesn't exercise this path.

Positive Highlights

  • ✅ Root cause properly addressed: context-aware html/template escaping replaces the fragile manual concatenation that triggered CodeQL alert #650.
  • ✅ Error from t.Execute is now propagated correctly.
  • ✅ Inline anonymous struct keeps the data shape local and readable.

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer · sonnet46 25.7 AIC · ⌖ 7.58 AIC · ⊞ 6.7K
Comment /matt to run again

return "https://github.com/settings/apps/new?state=" + state
}

const bootstrapRegistrationPageTmpl = `<!doctype html><html lang="en"><head><meta charset="utf-8"><title>Redirecting To GitHub App Creation</title></head><body><p>Redirecting to GitHub App creation...</p><form id="manifest-form" action="{{.Action}}" method="post"><input type="hidden" name="manifest" value="{{.Manifest}}"><noscript><button type="submit">Continue To GitHub App Creation</button></noscript></form><script>document.getElementById('manifest-form').submit();</script></body></html>`

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[/diagnosing-bugs] Template is re-parsed on every call — this allocates a new *template.Template on every page render and silently swallows a parse error at runtime.

💡 Suggested fix: compile once at package level with template.Must
var bootstrapRegistrationPage = template.Must(
    template.New("bootstrap").Parse(bootstrapRegistrationPageTmpl),
)

Then in the function body replace the parse block with just:

var buf bytes.Buffer
if err = bootstrapRegistrationPage.Execute(&buf, data); err != nil {
    return "", err
}

template.Must panics at program startup if the template literal is malformed — a far better failure mode than a silent error returned from an HTTP handler on first use.

@copilot please address this.

func renderBootstrapGitHubAppRegistrationPage(registrationURL string, manifest map[string]any) (string, error) {
encoded, err := json.Marshal(manifest)
if err != nil {
return "", err

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[/tdd] The existing test checks for "name" (attribute-escaped JSON) but doesn't cover a URL with query characters (?, &, =) in registrationURL. html/template URL-normalises the action attribute — if registrationURL ever contains characters that the URL sanitiser rejects it will silently substitute #ZgotmplZ, breaking the redirect silently.

💡 Suggested test case

Add a sub-test (or extend the existing one) that:

  1. Passes a registrationURL that contains query-string characters, e.g. https://github.com/settings/apps/new?state=abc&foo=bar
  2. Asserts the rendered action attribute equals the expected escaped URL
  3. Also verifies the manifest JSON double-quotes appear as " in the output

This guards against the #ZgotmplZ substitution and documents the expected escaping contract.

@copilot please address this.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Three issues to fix before merging

The html/template direction is correct — replacing manual concatenation with a context-aware template engine properly resolves the CodeQL alert. However, the implementation has three problems:

🔴 Issues found (3)
  1. Template re-parsed on every call (line 264): template.New(...).Parse(...) inside the function body re-parses a constant string on each invocation. Use a package-level var initialized with template.Must(...).

  2. Silent #ZgotmplZ corruption for unexpected URLs (line 264): html/template silently replaces URL-context values it deems unsafe with #ZgotmplZ, returning no error from Execute. Add a pre-execution URL scheme check or a post-execution #ZgotmplZ guard.

  3. htmlEscape function is now dead code (line 344): The only two call sites were removed by this change. The function and the "html" import should be deleted.

🔎 Code quality review by PR Code Quality Reviewer · sonnet46 43.4 AIC · ⌖ 4.85 AIC · ⊞ 5.7K
Comment /review to run again

Comments that could not be inline-anchored

pkg/cli/bootstrap_profile_helpers.go:264

Template re-parsed on every call: template.New(&quot;bootstrap&quot;).Parse(...) executes inside the function body, re-parsing a compile-time constant string on every invocation.

<details>
<summary>💡 Suggested fix</summary>

Parse once at package initialization using template.Must:

var bootstrapRegistrationPageTmpl = template.Must(
    template.New(&quot;bootstrap&quot;).Parse(`&lt;!doctype html&gt;...`),
)

Then in the function, just call bootstrapRegistrationPageTmpl.Execute(&amp;buf, data).

This e…

pkg/cli/bootstrap_profile_helpers.go:264

html/template silently replaces unsafe URLs with #ZgotmplZ: {{.Action}} is placed in the action attribute, which html/template treats as a URL context. If registrationURL is ever deemed unsafe (e.g. unexpected scheme), the engine silently substitutes #ZgotmplZ — producing a broken form with no error returned from t.Execute.

<details>
<summary>💡 Details and mitigation</summary>

The current registrationURL is https://github.com/settings/apps/new?state=&lt;hex&gt; which is saf…

pkg/cli/bootstrap_profile_helpers.go:344

htmlEscape is now dead code: the htmlEscape helper (and by extension the &quot;html&quot; import) is no longer called anywhere after this change removed the only two call sites.

<details>
<summary>💡 Suggested fix</summary>

Remove the dead function and the now-unused &quot;html&quot; import:

// Delete this:
func htmlEscape(value string) string {
    return html.EscapeString(value)
}

And remove &quot;html&quot; from the import block. The Go compiler will flag the unused import, but the dead functi…

@pelikhan

Copy link
Copy Markdown
Collaborator

@copilot run pr-finisher skill

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>

Copilot AI commented Jul 22, 2026

Copy link
Copy Markdown
Contributor Author

@copilot run pr-finisher skill

Ran pr-finisher workflow. Addressed the two unresolved bot review threads in commit 8312122 (pre-parsed bootstrap template at package scope and added action URL escaping coverage in bootstrap registration test), ran local validation (make fmt, make lint, make test-unit, make test, make recompile, and make agent-report-progress), and pushed the update. CI for this new HEAD still needs a maintainer re-trigger to fully verify checks.

@pelikhan
pelikhan merged commit 4c08e8e into main Jul 22, 2026
23 checks passed
@pelikhan
pelikhan deleted the copilot/fix-code-scanning-alert-650 branch July 22, 2026 15:56
@github-actions

Copy link
Copy Markdown
Contributor

🎉 This pull request is included in a new release.

Release: v0.83.0

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.

3 participants