go.lang.security.audit.net.formatted-template-string.formatted-template-string

Community Favorite
profile photo of semgrepsemgrep
Author
74,617
Download Count*

Found a formatted template string passed to 'template.HTML()'. 'template.HTML()' does not escape contents. Be absolutely sure there is no user-controlled data in this template. If user data can reach this template, you may have a XSS vulnerability.

Run Locally

Run in CI

Defintion

rules:
  - id: formatted-template-string
    message: Found a formatted template string passed to 'template.HTML()'.
      'template.HTML()' does not escape contents. Be absolutely sure there is no
      user-controlled data in this template. If user data can reach this
      template, you may have a XSS vulnerability.
    metadata:
      cwe:
        - "CWE-79: Improper Neutralization of Input During Web Page Generation
          ('Cross-site Scripting')"
      owasp:
        - A07:2017 - Cross-Site Scripting (XSS)
        - A03:2021 - Injection
      references:
        - https://golang.org/pkg/html/template/#HTML
      category: security
      technology:
        - go
      confidence: MEDIUM
      cwe2022-top25: true
      cwe2021-top25: true
      subcategory:
        - audit
      likelihood: LOW
      impact: MEDIUM
      license: Commons Clause License Condition v1.0[LGPL-2.1-only]
      vulnerability_class:
        - Cross-Site-Scripting (XSS)
    languages:
      - go
    severity: WARNING
    patterns:
      - pattern-not: template.HTML("..." + "...")
      - pattern-either:
          - pattern: template.HTML($T + $X, ...)
          - pattern: template.HTML(fmt.$P("...", ...), ...)
          - pattern: |
              $T = "..."
              ...
              $T = $FXN(..., $T, ...)
              ...
              template.HTML($T, ...)
          - pattern: |
              $T = fmt.$P("...", ...)
              ...
              template.HTML($T, ...)
          - pattern: |
              $T, $ERR = fmt.$P("...", ...)
              ...
              template.HTML($T, ...)
          - pattern: |
              $T = $X + $Y
              ...
              template.HTML($T, ...)
          - pattern: |-
              $T = "..."
              ...
              $OTHER, $ERR = fmt.$P(..., $T, ...)
              ...
              template.HTML($OTHER, ...)

Examples

formatted-template-string.go

package main

import (
	"fmt"
	"html/template"
	"net/http"
	"strconv"
)

func Fine(r *http.Request) template.HTML {
	// ok: formatted-template-string
	return template.HTML("<html><body><h1>Hello, world</h1></body></html>")
}

func AlsoFine(r *http.Request) template.HTML {
	// ok: formatted-template-string
	return template.HTML("<html><body><h1>" + "Hello, world</h1></body></html>")
}

func Concat(r *http.Request) template.HTML {
	customerId := r.URL.Query().Get("id")
	// ruleid: formatted-template-string
	tmpl := "<html><body><h1>" + customerId + "</h1></body></html>"

	return template.HTML(tmpl)
}

func ConcatBranch(r *http.Request) template.HTML {
	customerId := r.URL.Query().Get("id")
	doIt, err := strconv.ParseBool(r.URL.Query().Get("do"))
	if err != nil {
		return template.HTML("")
	}
	var tmpl string
	if doIt {
		// todo: formatted-template-string
		tmpl = "<html><body><h1>" + customerId + "</h1></body></html>"
	} else {
		tmpl = ""
	}

	return template.HTML(tmpl)
}

func ConcatInline(r *http.Request) template.HTML {
	customerId := r.URL.Query().Get("id")

	// ruleid: formatted-template-string
	return template.HTML("<html><body><h1>" + customerId + "</h1></body></html>")
}

func ConcatInlineOneside(r *http.Request) template.HTML {
	customerId := r.URL.Query().Get("id")

	// ruleid: formatted-template-string
	return template.HTML("<html><body><h1>" + customerId)
}

func Formatted(r *http.Request) template.HTML {
	customerId := r.URL.Query().Get("id")
	// ruleid: formatted-template-string
	tmpl, err := fmt.Printf("<html><body><h1>%s</h1></body></html>", customerId)
	if err != nil {
		return template.HTML("")
	}
	return template.HTML(tmpl)
}

func FormattedInline(r *http.Request) template.HTML {
	customerId := r.URL.Query().Get("id")
	// ruleid: formatted-template-string
	return template.HTML(fmt.Sprintf("<html><body><h1>%s</h1></body></html>", customerId))
}

func main() {}