Add security-severity to metadata in NodeJS rules
What does this MR do?
The NodeJS scanner handles severity differently from our other scanners, specifically semgrep where the rules are moving to.
NodeJS Scan maps severity as:
func severityToLevel(severity string) report.SeverityLevel {
switch severity {
case "INFO":
return report.SeverityLevelInfo
case "WARNING":
return report.SeverityLevelMedium
case "ERROR":
return report.SeverityLevelHigh
}
return report.SeverityLevelUnknown
}
This means a rule with the severity ERROR
will be displayed in the vulnerability report as severity High
.
This differs from semgrep that maps the severities as:
switch r.DefaultConfiguration.Level {
case "error":
return SeverityLevelCritical
case "warning":
return SeverityLevelMedium
case "note":
return SeverityLevelInfo
case "none":
return SeverityLevelInfo
default:
return SeverityLevelMedium
}
This difference means that a rule imported from NodeJS Scan to semgrep with the severity of ERROR
will show as a severity Critical
, rather than High
, as it was when the same rule ran in NodeJS Scan.
This MR addresses this by adding the security-severity
field in metadata
, as described in Include security severity levels in semgrep rules (gitlab-org/gitlab#398574 - closed) • Unassigned • 17.0.
Adding the metadata.security-severity
field overrides the severity
attribute and returns that in the semgrep.sarif
file generated by semgrep. This is then correctly converted in the gl-sast-report.json
.
I've tested this by updating the NodeJS Scan ruleset with metadata.security-severity
and releasing in craigmsmith-fix-nodejs-severity-bfe89ae6bc9a4a8ea96091f97ea0287702e588b2. When installing this in semgrep and running on the nodeJS scan default fixture the resulting gl-sast-report.json
showed the severity as HIGH
Generated gl-sast-report.json
{
"version": "15.0.7",
"vulnerabilities": [
{
"id": "937ac7a208f995e16c103004da8aed75673cacd222dae6a9f04d6347e00b00e9",
"category": "sast",
"name": "Semgrep Finding: javascript-redirect-rule-express_open_redirect",
"description": "Untrusted user input in redirect() can result in Open Redirect vulnerability.\n",
"cve": "semgrep_id:javascript-redirect-rule-express_open_redirect:46:46",
"severity": "High",
"scanner": {
"id": "semgrep",
"name": "Semgrep"
},
"location": {
"file": "subdir/index.js",
"start_line": 46
},
"identifiers": [
{
"type": "semgrep_id",
"name": "javascript-redirect-rule-express_open_redirect",
"value": "javascript-redirect-rule-express_open_redirect"
},
{
"type": "cwe",
"name": "CWE-601",
"value": "601",
"url": "https://cwe.mitre.org/data/definitions/601.html"
},
{
"type": "owasp",
"name": "A1:2017 - Injection",
"value": "A1:2017"
}
]
},
{
"id": "42077986ee522d5e936137b0b262efc5c2cc9c19a5d2aa8b0405304e1d9449bd",
"category": "sast",
"name": "Semgrep Finding: javascript-redirect-rule-express_open_redirect",
"description": "Untrusted user input in redirect() can result in Open Redirect vulnerability.\n",
"cve": "semgrep_id:javascript-redirect-rule-express_open_redirect:51:51",
"severity": "High",
"scanner": {
"id": "semgrep",
"name": "Semgrep"
},
"location": {
"file": "index.js",
"start_line": 51
},
"identifiers": [
{
"type": "semgrep_id",
"name": "javascript-redirect-rule-express_open_redirect",
"value": "javascript-redirect-rule-express_open_redirect"
},
{
"type": "cwe",
"name": "CWE-601",
"value": "601",
"url": "https://cwe.mitre.org/data/definitions/601.html"
},
{
"type": "owasp",
"name": "A1:2017 - Injection",
"value": "A1:2017"
}
]
},
{
"id": "8347660f5b977807af8e3fa1a1d2ab92e08a5ebf721c7f57c6c8620e231c9f1f",
"category": "sast",
"name": "Semgrep Finding: javascript-xss-rule-express_xss",
"description": "Untrusted User Input in Response will result in Reflected Cross Site Scripting Vulnerability.\n",
"cve": "semgrep_id:javascript-xss-rule-express_xss:28:28",
"severity": "High",
"scanner": {
"id": "semgrep",
"name": "Semgrep"
},
"location": {
"file": "index.js",
"start_line": 28
},
"identifiers": [
{
"type": "semgrep_id",
"name": "javascript-xss-rule-express_xss",
"value": "javascript-xss-rule-express_xss"
},
{
"type": "cwe",
"name": "CWE-79",
"value": "79",
"url": "https://cwe.mitre.org/data/definitions/79.html"
},
{
"type": "owasp",
"name": "A1:2017 - Injection",
"value": "A1:2017"
}
]
},
{
"id": "a196c01a47d7f4cce00f9123943139c7138f86dd8b69d126bf89cc60e47879d6",
"category": "sast",
"name": "Semgrep Finding: javascript-xss-rule-express_xss",
"description": "Untrusted User Input in Response will result in Reflected Cross Site Scripting Vulnerability.\n",
"cve": "semgrep_id:javascript-xss-rule-express_xss:28:28",
"severity": "High",
"scanner": {
"id": "semgrep",
"name": "Semgrep"
},
"location": {
"file": "subdir/index.js",
"start_line": 28
},
"identifiers": [
{
"type": "semgrep_id",
"name": "javascript-xss-rule-express_xss",
"value": "javascript-xss-rule-express_xss"
},
{
"type": "cwe",
"name": "CWE-79",
"value": "79",
"url": "https://cwe.mitre.org/data/definitions/79.html"
},
{
"type": "owasp",
"name": "A1:2017 - Injection",
"value": "A1:2017"
}
]
},
{
"id": "44093d9680057daaea3eaf46582dd271a70f88f67689454a0504b06a79d9fb81",
"category": "sast",
"name": "Semgrep Finding: javascript-xss-rule-express_xss",
"description": "Untrusted User Input in Response will result in Reflected Cross Site Scripting Vulnerability.\n",
"cve": "semgrep_id:javascript-xss-rule-express_xss:32:32",
"severity": "High",
"scanner": {
"id": "semgrep",
"name": "Semgrep"
},
"location": {
"file": "subdir/index.js",
"start_line": 32
},
"identifiers": [
{
"type": "semgrep_id",
"name": "javascript-xss-rule-express_xss",
"value": "javascript-xss-rule-express_xss"
},
{
"type": "cwe",
"name": "CWE-79",
"value": "79",
"url": "https://cwe.mitre.org/data/definitions/79.html"
},
{
"type": "owasp",
"name": "A1:2017 - Injection",
"value": "A1:2017"
}
]
},
{
"id": "aa3489ae9a0df69486eb46412d75851ce9e21252dbbcdb1c29b5ea6d3e31de38",
"category": "sast",
"name": "Semgrep Finding: javascript-xss-rule-express_xss",
"description": "Untrusted User Input in Response will result in Reflected Cross Site Scripting Vulnerability.\n",
"cve": "semgrep_id:javascript-xss-rule-express_xss:32:32",
"severity": "High",
"scanner": {
"id": "semgrep",
"name": "Semgrep"
},
"location": {
"file": "index.js",
"start_line": 32
},
"identifiers": [
{
"type": "semgrep_id",
"name": "javascript-xss-rule-express_xss",
"value": "javascript-xss-rule-express_xss"
},
{
"type": "cwe",
"name": "CWE-79",
"value": "79",
"url": "https://cwe.mitre.org/data/definitions/79.html"
},
{
"type": "owasp",
"name": "A1:2017 - Injection",
"value": "A1:2017"
}
]
},
{
"id": "7a0a8d47c41cc4897025914fc08f3652eb256fe4c7e2dd6b0628f292f7ce78f4",
"category": "sast",
"name": "Semgrep Finding: javascript-crypto-rule-node_insecure_random_generator",
"description": "crypto.pseudoRandomBytes()/Math.random() is a cryptographically weak random number generator.\n",
"cve": "semgrep_id:javascript-crypto-rule-node_insecure_random_generator:42:42",
"severity": "Medium",
"scanner": {
"id": "semgrep",
"name": "Semgrep"
},
"location": {
"file": "index.js",
"start_line": 42
},
"identifiers": [
{
"type": "semgrep_id",
"name": "javascript-crypto-rule-node_insecure_random_generator",
"value": "javascript-crypto-rule-node_insecure_random_generator"
},
{
"type": "cwe",
"name": "CWE-327",
"value": "327",
"url": "https://cwe.mitre.org/data/definitions/327.html"
},
{
"type": "owasp",
"name": "A9:2017 - Using Components with Known Vulnerabilities",
"value": "A9:2017"
}
]
}
],
"dependency_files": [],
"scan": {
"analyzer": {
"id": "semgrep",
"name": "Semgrep",
"url": "https://gitlab.com/gitlab-org/security-products/analyzers/semgrep",
"vendor": {
"name": "GitLab"
},
"version": "4.11.0"
},
"scanner": {
"id": "semgrep",
"name": "Semgrep",
"url": "https://github.com/returntocorp/semgrep",
"vendor": {
"name": "GitLab"
},
"version": "1.56.0"
},
"type": "sast",
"start_time": "2024-02-05T03:26:34",
"end_time": "2024-02-05T03:26:38",
"status": "success"
}
}
When the same test runs but with only a ruleset that includes only original severity field, the report shows the severity as CRITICAL
.
Generated gl-sast-report.json
{
"version": "15.0.7",
"vulnerabilities": [
{
"id": "1b438ced3d7a3e165767f49f8de9c8235b6b686299879ae880d7c8c52af7fc92",
"category": "sast",
"name": "Semgrep Finding: javascript-redirect-rule-express_open_redirect",
"description": "Untrusted user input in redirect() can result in Open Redirect vulnerability.\n",
"cve": "semgrep_id:javascript-redirect-rule-express_open_redirect:46:46",
"severity": "Critical",
"scanner": {
"id": "semgrep",
"name": "Semgrep"
},
"location": {
"file": "subdir/index.js",
"start_line": 46
},
"identifiers": [
{
"type": "semgrep_id",
"name": "javascript-redirect-rule-express_open_redirect",
"value": "javascript-redirect-rule-express_open_redirect"
},
{
"type": "cwe",
"name": "CWE-601",
"value": "601",
"url": "https://cwe.mitre.org/data/definitions/601.html"
},
{
"type": "owasp",
"name": "A1:2017 - Injection",
"value": "A1:2017"
}
]
},
{
"id": "296ed9ef674fdcf56fe6992422bcb70063595dc299c08e2ae83fe2f817984310",
"category": "sast",
"name": "Semgrep Finding: javascript-redirect-rule-express_open_redirect",
"description": "Untrusted user input in redirect() can result in Open Redirect vulnerability.\n",
"cve": "semgrep_id:javascript-redirect-rule-express_open_redirect:51:51",
"severity": "Critical",
"scanner": {
"id": "semgrep",
"name": "Semgrep"
},
"location": {
"file": "index.js",
"start_line": 51
},
"identifiers": [
{
"type": "semgrep_id",
"name": "javascript-redirect-rule-express_open_redirect",
"value": "javascript-redirect-rule-express_open_redirect"
},
{
"type": "cwe",
"name": "CWE-601",
"value": "601",
"url": "https://cwe.mitre.org/data/definitions/601.html"
},
{
"type": "owasp",
"name": "A1:2017 - Injection",
"value": "A1:2017"
}
]
},
{
"id": "53de400182e61bdddb81e6f2cb4658fcefb4989ba5391d95056fc98b1ae3e4e2",
"category": "sast",
"name": "Semgrep Finding: javascript-xss-rule-express_xss",
"description": "Untrusted User Input in Response will result in Reflected Cross Site Scripting Vulnerability.\n",
"cve": "semgrep_id:javascript-xss-rule-express_xss:28:28",
"severity": "Critical",
"scanner": {
"id": "semgrep",
"name": "Semgrep"
},
"location": {
"file": "index.js",
"start_line": 28
},
"identifiers": [
{
"type": "semgrep_id",
"name": "javascript-xss-rule-express_xss",
"value": "javascript-xss-rule-express_xss"
},
{
"type": "cwe",
"name": "CWE-79",
"value": "79",
"url": "https://cwe.mitre.org/data/definitions/79.html"
},
{
"type": "owasp",
"name": "A1:2017 - Injection",
"value": "A1:2017"
}
]
},
{
"id": "2df15ebf1f829b7bfab1d9370a516588f835f1afe86a48b7fcdeaf1c9f18fbbe",
"category": "sast",
"name": "Semgrep Finding: javascript-xss-rule-express_xss",
"description": "Untrusted User Input in Response will result in Reflected Cross Site Scripting Vulnerability.\n",
"cve": "semgrep_id:javascript-xss-rule-express_xss:28:28",
"severity": "Critical",
"scanner": {
"id": "semgrep",
"name": "Semgrep"
},
"location": {
"file": "subdir/index.js",
"start_line": 28
},
"identifiers": [
{
"type": "semgrep_id",
"name": "javascript-xss-rule-express_xss",
"value": "javascript-xss-rule-express_xss"
},
{
"type": "cwe",
"name": "CWE-79",
"value": "79",
"url": "https://cwe.mitre.org/data/definitions/79.html"
},
{
"type": "owasp",
"name": "A1:2017 - Injection",
"value": "A1:2017"
}
]
},
{
"id": "ff0a7be9241facf81e4673a008513d5530d7ad92dbdea7dc986862260e8eb63e",
"category": "sast",
"name": "Semgrep Finding: javascript-xss-rule-express_xss",
"description": "Untrusted User Input in Response will result in Reflected Cross Site Scripting Vulnerability.\n",
"cve": "semgrep_id:javascript-xss-rule-express_xss:32:32",
"severity": "Critical",
"scanner": {
"id": "semgrep",
"name": "Semgrep"
},
"location": {
"file": "subdir/index.js",
"start_line": 32
},
"identifiers": [
{
"type": "semgrep_id",
"name": "javascript-xss-rule-express_xss",
"value": "javascript-xss-rule-express_xss"
},
{
"type": "cwe",
"name": "CWE-79",
"value": "79",
"url": "https://cwe.mitre.org/data/definitions/79.html"
},
{
"type": "owasp",
"name": "A1:2017 - Injection",
"value": "A1:2017"
}
]
},
{
"id": "7a21cbe7f4aa6ea61c3aa24d6326057b0df29551d7db54e8f929c2f47df4eba6",
"category": "sast",
"name": "Semgrep Finding: javascript-xss-rule-express_xss",
"description": "Untrusted User Input in Response will result in Reflected Cross Site Scripting Vulnerability.\n",
"cve": "semgrep_id:javascript-xss-rule-express_xss:32:32",
"severity": "Critical",
"scanner": {
"id": "semgrep",
"name": "Semgrep"
},
"location": {
"file": "index.js",
"start_line": 32
},
"identifiers": [
{
"type": "semgrep_id",
"name": "javascript-xss-rule-express_xss",
"value": "javascript-xss-rule-express_xss"
},
{
"type": "cwe",
"name": "CWE-79",
"value": "79",
"url": "https://cwe.mitre.org/data/definitions/79.html"
},
{
"type": "owasp",
"name": "A1:2017 - Injection",
"value": "A1:2017"
}
]
},
{
"id": "7a0a8d47c41cc4897025914fc08f3652eb256fe4c7e2dd6b0628f292f7ce78f4",
"category": "sast",
"name": "Semgrep Finding: javascript-crypto-rule-node_insecure_random_generator",
"description": "crypto.pseudoRandomBytes()/Math.random() is a cryptographically weak random number generator.\n",
"cve": "semgrep_id:javascript-crypto-rule-node_insecure_random_generator:42:42",
"severity": "Medium",
"scanner": {
"id": "semgrep",
"name": "Semgrep"
},
"location": {
"file": "index.js",
"start_line": 42
},
"identifiers": [
{
"type": "semgrep_id",
"name": "javascript-crypto-rule-node_insecure_random_generator",
"value": "javascript-crypto-rule-node_insecure_random_generator"
},
{
"type": "cwe",
"name": "CWE-327",
"value": "327",
"url": "https://cwe.mitre.org/data/definitions/327.html"
},
{
"type": "owasp",
"name": "A9:2017 - Using Components with Known Vulnerabilities",
"value": "A9:2017"
}
]
}
],
"dependency_files": [],
"scan": {
"analyzer": {
"id": "semgrep",
"name": "Semgrep",
"url": "https://gitlab.com/gitlab-org/security-products/analyzers/semgrep",
"vendor": {
"name": "GitLab"
},
"version": "4.11.0"
},
"scanner": {
"id": "semgrep",
"name": "Semgrep",
"url": "https://github.com/returntocorp/semgrep",
"vendor": {
"name": "GitLab"
},
"version": "1.56.0"
},
"type": "sast",
"start_time": "2024-02-05T03:38:52",
"end_time": "2024-02-05T03:38:57",
"status": "success"
}
}
Why is the original severity field still included in the report?
Removing the severity
field causes the semgrep validity check to fail.
What are the relevant issue numbers?
- Migrate NodeJS scan rules to Semgrep-based anal... (gitlab-org/gitlab#395487 - closed) • Craig Smith • 17.0 • Needs attention
- Include security severity levels in semgrep rules (gitlab-org/gitlab#398574 - closed) • Unassigned • 17.0
Does this MR meet the acceptance criteria?
-
Changelog entry added -
Documentation created/updated for GitLab EE, if necessary -
Documentation created/updated for this project, if necessary -
Documentation reviewed by technical writer or follow-up review issue created -
Tests added for this feature/bug -
Job definition updated, if necessary -
Conforms to the code review guidelines -
Conforms to the Go guidelines -
Security reports checked/validated by reviewer