VYPR
Moderate severityNVD Advisory· Published Dec 13, 2022· Updated Apr 22, 2025

CVE-2022-43996

CVE-2022-43996

Description

The csaf_provider package before 0.8.2 allows XSS via a crafted CSAF document uploaded as text/html. The endpoint upload allows valid CSAF advisories (JSON format) to be uploaded with Content-Type text/html and filenames ending in .html. When subsequently accessed via web browser, these advisories are served and interpreted as HTML pages. Such uploaded advisories can contain JavaScript code that will execute within the browser context of users inspecting the advisory.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

CSAF provider before 0.8.2 allows stored XSS via uploaded CSAF advisories with Content-Type text/html and .html extension.

Vulnerability

Overview

The csaf_provider package, part of the csaf_distribution tools [4], contains a stored cross-site scripting (XSS) vulnerability in versions before 0.8.2. The upload endpoint accepts valid CSAF advisories in JSON format but fails to validate the Content-Type header and filename extension. An attacker can upload a CSAF document with Content-Type: text/html and a filename ending in .html, causing the server to serve the advisory as an HTML page [1].

Exploitation

No authentication is required to upload a CSAF document to the provider's management endpoint. An attacker crafts a valid CSAF advisory (JSON structure that passes schema validation) and includes malicious JavaScript within the advisory content. The file is uploaded with the text/html MIME type and an .html extension. When other users (e.g., security analysts, administrators) view the uploaded advisory through their web browser, the server returns the file with an HTML content type, and the browser renders it as a web page, executing the embedded JavaScript [1].

Impact

Successful exploitation allows an attacker to execute arbitrary JavaScript in the context of the victim's browser session on the CSAF provider domain. This can lead to theft of session cookies, exfiltration of sensitive data displayed on the page, or performing actions on behalf of the authenticated user. The vulnerability is classified as stored XSS because the malicious payload persists on the server and affects all subsequent viewers of the advisory [1].

Mitigation

The vulnerability is fixed in csaf_provider version 0.8.2 [3]. The fix introduces a filename conformity check that rejects uploaded files whose names do not conform to the CSAF standard for advisory filenames (e.g., requiring the .json extension) [2]. Users should upgrade to csaf_provider 0.8.2 or later. There are no known workarounds; ensuring the upload endpoint is not exposed to untrusted users may reduce risk but does not eliminate it.

AI Insight generated on May 20, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
github.com/csaf-poc/csaf_distributionGo
< 0.8.20.8.2

Affected products

3

Patches

1
17f22855ee8d

Add filename conformity check

https://github.com/csaf-poc/csaf_distributionSascha L. TeichmannMay 20, 2022via ghsa
6 files changed · +100 10
  • cmd/csaf_aggregator/mirror.go+7 1 modified
    @@ -419,7 +419,13 @@ func (w *worker) mirrorFiles(tlpLabel *csaf.TLPLabel, files []string) error {
     			log.Printf("error: %s\n", err)
     			continue
     		}
    -		filename := util.CleanFileName(filepath.Base(u.Path))
    +
    +		// Ignore not confirming filenames.
    +		filename := filepath.Base(u.Path)
    +		if !util.ConfirmingFileName(filename) {
    +			log.Printf("Not confirming filename %q. Ignoring.\n", filename)
    +			continue
    +		}
     
     		var advisory interface{}
     
    
  • cmd/csaf_checker/processor.go+25 1 modified
    @@ -22,6 +22,7 @@ import (
     	"log"
     	"net/http"
     	"net/url"
    +	"path/filepath"
     	"regexp"
     	"sort"
     	"strconv"
    @@ -204,6 +205,7 @@ func (p *processor) checkDomain(domain string) error {
     		(*processor).checkSecurity,
     		(*processor).checkCSAFs,
     		(*processor).checkMissing,
    +		(*processor).checkInvalid,
     		(*processor).checkListing,
     		(*processor).checkWellknownMetadataReporter,
     		(*processor).checkDNSPathReporter,
    @@ -724,7 +726,29 @@ func (p *processor) checkMissing(string) error {
     	return nil
     }
     
    -// checkListing wents over all found adivisories URLs and checks,
    +// checkInvalid wents over all found adivisories URLs and checks
    +// if file name confirms to standard.
    +func (p *processor) checkInvalid(string) error {
    +
    +	p.badDirListings.use()
    +	var invalids []string
    +
    +	for f := range p.alreadyChecked {
    +		if !util.ConfirmingFileName(filepath.Base(f)) {
    +			invalids = append(invalids, f)
    +		}
    +	}
    +
    +	if len(invalids) > 0 {
    +		sort.Strings(invalids)
    +		p.badDirListings.add("advisories with invalid file names: %s",
    +			strings.Join(invalids, ", "))
    +	}
    +
    +	return nil
    +}
    +
    +// checkListing wents over all found adivisories URLs and checks
     // if their parent directory is listable.
     func (p *processor) checkListing(string) error {
     
    
  • cmd/csaf_provider/actions.go+4 0 modified
    @@ -39,6 +39,10 @@ func (c *controller) loadCSAF(r *http.Request) (string, []byte, error) {
     	}
     	defer file.Close()
     
    +	if !util.ConfirmingFileName(handler.Filename) {
    +		return "", nil, errors.New("given csaf filename is not confirming")
    +	}
    +
     	var buf bytes.Buffer
     	if _, err := io.Copy(&buf, c.cfg.uploadLimiter(file)); err != nil {
     		return "", nil, err
    
  • cmd/csaf_uploader/main.go+4 0 modified
    @@ -277,6 +277,10 @@ func (p *processor) uploadRequest(filename string) (*http.Request, error) {
     // It prints the response messages.
     func (p *processor) process(filename string) error {
     
    +	if bn := filepath.Base(filename); !util.ConfirmingFileName(bn) {
    +		return fmt.Errorf("%q is not a confirming file name", bn)
    +	}
    +
     	req, err := p.uploadRequest(filename)
     	if err != nil {
     		return err
    
  • util/file.go+18 8 modified
    @@ -19,15 +19,25 @@ import (
     	"time"
     )
     
    -var (
    -	twoOrMoreDots = regexp.MustCompile(`\.{2,}`)
    -	stripSlashes  = strings.NewReplacer(`/`, ``, `\`, ``)
    -)
    -
    -// CleanFileName removes the "/" "\" charachters and replace the two or more
    -// occurences of "." with only one from the passed string.
    +var invalidRune = regexp.MustCompile(`[^+\-a-z0-9]+`) // invalid runes + `_`
    +
    +// CleanFileName replaces invalid runes with an underscore and
    +// afterwards collapses multiple underscores into one.
    +// If the filename does not end with '.json' it will be appended.
    +// The filename is converted to lower case.
    +// https://docs.oasis-open.org/csaf/csaf/v2.0/csd02/csaf-v2.0-csd02.html#51-filename
    +// specifies valid runes as 'a' to 'z', '0' to '9' and '+', '-', '_'.
     func CleanFileName(s string) string {
    -	return twoOrMoreDots.ReplaceAllString(stripSlashes.Replace(s), `.`)
    +	s = strings.ToLower(s)
    +	if strings.HasSuffix(s, ".json") {
    +		s = s[:len(s)-len(".json")]
    +	}
    +	return invalidRune.ReplaceAllString(s, "_") + ".json"
    +}
    +
    +// ConfirmingFileName checks if the given filename is confirming the standard.
    +func ConfirmingFileName(fname string) bool {
    +	return fname == CleanFileName(fname)
     }
     
     // PathExists returns true if path exits.
    
  • util/file_test.go+42 0 modified
    @@ -5,6 +5,48 @@ import (
     	"testing"
     )
     
    +func TestCleanFileName(t *testing.T) {
    +	for _, x := range [][2]string{
    +		{`HELLO`, `hello.json`},
    +		{`hello`, `hello.json`},
    +		{`cisco-sa-20190513-secureboot.json`, `cisco-sa-20190513-secureboot.json`},
    +		{``, `.json`},
    +		{`..`, `_.json`},
    +		{`../..`, `_.json`},
    +		{`abc.html`, `abc_html.json`},
    +		{`abc_.htm__l`, `abc_htm_l.json`},
    +		{`foo+BAR`, `foo+bar.json`},
    +	} {
    +		if got := CleanFileName(x[0]); got != x[1] {
    +			t.Errorf("%q: Expected %q but got %q.", x[0], x[1], got)
    +		}
    +	}
    +}
    +
    +func TestConfirmingFileName(t *testing.T) {
    +	for _, x := range []struct {
    +		s string
    +		b bool
    +	}{
    +		{`HELLO`, false},
    +		{`hello`, false},
    +		{`cisco-sa-20190513-secureboot.json`, true},
    +		{`example_company_-_2019-yh3234.json`, true},
    +		{`rhba-2019_0024.json`, true},
    +		{`2022__01-a.json`, false},
    +		{``, false},
    +		{`..`, false},
    +		{`../..`, false},
    +		{`abc.html`, false},
    +		{`abc_.htm__l`, false},
    +		{`foo+BAR`, false},
    +	} {
    +		if got := ConfirmingFileName(x.s); got != x.b {
    +			t.Errorf("%q: Expected %t but got %t.", x.s, x.b, got)
    +		}
    +	}
    +}
    +
     func TestNWriter(t *testing.T) {
     
     	msg := []byte("Gruß!\n")
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

5

News mentions

0

No linked articles in our index yet.