go.lang.security.injection.raw-html-format.raw-html-format

profile photo of semgrepsemgrep
Author
unknown
Download Count*

Detected user input flowing into a manually constructed HTML string. You may be accidentally bypassing secure methods of rendering HTML by manually constructing HTML and this could create a cross-site scripting vulnerability, which could let attackers steal sensitive user data. Use the html/template package which will safely render HTML instead, or inspect that the HTML is rendered safely.

Run Locally

Run in CI

Defintion

rules:
  - id: raw-html-format
    languages:
      - go
    severity: WARNING
    message: Detected user input flowing into a manually constructed HTML string.
      You may be accidentally bypassing secure methods of rendering HTML by
      manually constructing HTML and this could create a cross-site scripting
      vulnerability, which could let attackers steal sensitive user data. Use
      the `html/template` package which will safely render HTML instead, or
      inspect that the HTML is rendered safely.
    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
      category: security
      technology:
        - go
      references:
        - https://blogtitle.github.io/robn-go-security-pearls-cross-site-scripting-xss/
      license: Commons Clause License Condition v1.0[LGPL-2.1-only]
      confidence: MEDIUM
      cwe2022-top25: true
      cwe2021-top25: true
      subcategory:
        - vuln
      likelihood: HIGH
      impact: MEDIUM
      vulnerability_class:
        - Cross-Site-Scripting (XSS)
    mode: taint
    pattern-sources:
      - patterns:
          - pattern-either:
              - pattern: |
                  ($REQUEST : *http.Request).$ANYTHING
              - pattern: |
                  ($REQUEST : http.Request).$ANYTHING
          - metavariable-regex:
              metavariable: $ANYTHING
              regex: ^(BasicAuth|Body|Cookie|Cookies|Form|FormValue|GetBody|Host|MultipartReader|ParseForm|ParseMultipartForm|PostForm|PostFormValue|Referer|RequestURI|Trailer|TransferEncoding|UserAgent|URL)$
    pattern-sanitizers:
      - pattern: html.EscapeString(...)
    pattern-sinks:
      - patterns:
          - pattern-either:
              - pattern: fmt.Printf("$HTMLSTR", ...)
              - pattern: fmt.Sprintf("$HTMLSTR", ...)
              - pattern: fmt.Fprintf($W, "$HTMLSTR", ...)
              - pattern: '"$HTMLSTR" + ...'
          - metavariable-pattern:
              metavariable: $HTMLSTR
              language: generic
              pattern: <$TAG ...

Examples

raw-html-format.go

package main

import (
    "fmt"
    "log"
    "net/http"
)

func getMovieQuote() map[string]string {
    m := make(map[string]string)
    m["quote"] = "I'll be back."
    m["movie"] = "The Terminator"
    m["year"] = "1984"

    return m
}

func healthCheck(w http.ResponseWriter, r *http.Request) {
    // ok: raw-html-format
    w.Write([]byte("alive"))
}

func indexPage(w http.ResponseWriter, r *http.Request) {
    const tme = `<html>`

    const template = `
    <html>
    <body>
      <h1>Random Movie Quotes</h1>
      <h2>%s</h2>
      <h4>~%s, %s</h4>
    </body>
    </html>`

    quote := getMovieQuote()

    quoteText := quote["quote"]
    movie := quote["movie"]
    year := quote["year"]

    w.WriteHeader(http.StatusAccepted)
    // ok: raw-html-format
    w.Write([]byte(fmt.Sprintf(template, quoteText, movie, year)))
}

func errorPage(w http.ResponseWriter, r *http.Request) {
    params := r.URL.Query()
    urls, ok := params["url"]
    if !ok {
        log.Println("Error")
        return
    }
    url := urls[0]

    const template = `
    <html>
    <body>
      <h1>error; page not found. <a href="%s">go back</a></h1>
    </body>
    </html>`

    w.WriteHeader(http.StatusAccepted)
    // ruleid:raw-html-format
    w.Write([]byte(fmt.Sprintf(template, url)))
}

func errorPage2(w http.ResponseWriter, r *http.Request) {
    params := r.URL.Query()
    urls, ok := params["url"]
    if !ok {
        log.Println("Error")
        return
    }
    url := urls[0]

    const template = `
    <html>
    <body>
      <h1>error; page not found. <a href="%s">go back</a></h1>
    </body>
    </html>`

    w.WriteHeader(http.StatusAccepted)
    // ruleid:raw-html-format
    w.Write([]byte(fmt.Printf(template, url)))
}

func errorPage3(w http.ResponseWriter, r *http.Request) {
    params := r.URL.Query()
    urls, ok := params["url"]
    if !ok {
        log.Println("Error")
        return
    }
    url := urls[0]

    const template = `
    <html>
    <body>
      <h1>error; page not found. <a href="%s">go back</a></h1>
    </body>
    </html>`

    w.WriteHeader(http.StatusAccepted)
    // ruleid:raw-html-format
    fmt.Fprintf(w, template, url)
}

func errorPage4(w http.ResponseWriter, r *http.Request) {
    params := r.URL.Query()
    urls, ok := params["url"]
    if !ok {
        log.Println("Error")
        return
    }
    url := urls[0]

    // ruleid:raw-html-format
    const template = "<html><body><h1>error; page not found. <a href='" + url + "'>go back </a></h1>"

    w.WriteHeader(http.StatusAccepted)
    w.Write([]byte(template))
}

func main() {
    http.HandleFunc("/", indexPage)
    http.HandleFunc("/error", errorPage)
    http.ListenAndServe(":8080", nil)
}