Security Post-it #6 – Software Security Testing for JavaScript and TypeScript
In this short security post-it, I explain how to secure your JavaScript and TypeScript code using open-source tools: SAST and SCA.
Nov 3, 2022 by Nicolas Béguier
We all agree that you have to secure your code. On the other hand, it is not always easy to know where to start and identify what is effective. I will present here the SAST and SCA implementations on your
JavaScript and TypeScript repositories.
Oh wait, I forgot to tell you. This is to build it yourself on your CI/CD or in your pre-commit hooks, with open-source tools.
Oh wait, I forgot to tell you. This is to build it yourself on your CI/CD or in your pre-commit hooks, with open-source tools.
What are SAST and SCA ?
Static Application Security Testing (SAST) is used to scan the code you write for security vulnerabilities and Software Composition Analysis (SCA) is used to scan your dependencies for security vulnerabilities.
When we look at these two technologies side by side, it becomes clear that both are necessary for an effective and secure development approach. SAST is more helpful for the code you write, while SCA is effective at analyzing the open-source software your organization uses, as well as its dependencies. These two technologies address security issues early and often during the development cycle.
In my opinion, SAST should be both asynchronous and synchronous. Blocking CI/CD pipelines to prevent unsecured code is great, but you need to specify exceptions and false-positive management. Running it asynchronously permits your security team to read reports and be alerted in real-time for potential vulnerabilities.
SCA must be asynchronous only, otherwise you will only be alerted when new code is pushed. Dead repositories are full of vulnerable dependencies. Run it somewhere else but continuously, and why not use a tool to create Pull Requests automatically, like Renovabot.
When we look at these two technologies side by side, it becomes clear that both are necessary for an effective and secure development approach. SAST is more helpful for the code you write, while SCA is effective at analyzing the open-source software your organization uses, as well as its dependencies. These two technologies address security issues early and often during the development cycle.
In my opinion, SAST should be both asynchronous and synchronous. Blocking CI/CD pipelines to prevent unsecured code is great, but you need to specify exceptions and false-positive management. Running it asynchronously permits your security team to read reports and be alerted in real-time for potential vulnerabilities.
SCA must be asynchronous only, otherwise you will only be alerted when new code is pushed. Dead repositories are full of vulnerable dependencies. Run it somewhere else but continuously, and why not use a tool to create Pull Requests automatically, like Renovabot.
Static Application Security Testing (SAST)
Today, if you want really effective security coverage, you need to use semgrep. I can propose
njsscan which relies on semgrep with additionnal rules.
Njsscan focuses only on server side javascript/html code, while you are interested in client-side security (Ex: DOM-XSS, postmessage abuse, open redirect) and extended rules.
Njsscan focuses only on server side javascript/html code, while you are interested in client-side security (Ex: DOM-XSS, postmessage abuse, open redirect) and extended rules.
Install
# Semgrep
$ pip install semgrep # Njsscan
$ pip install njsscan
$ pip install semgrep # Njsscan
$ pip install njsscan
Audit with semgrep
Human readable output
# Paranoid mode: OWASP Top Ten + some of my favorites
$ semgrep --config=r/javascript.browser.security.insufficient-postmessage-origin-validation.insufficient-postmessage-origin-validation --config=r/javascript.lang.security.audit.incomplete-sanitization.incomplete-sanitization --config=r/javascript.browser.security.dom-based-xss.dom-based-xss --config=r/typescript.react.security.audit.react-css-injection.react-css-injection --config=r/typescript.react.security.audit.react-href-var.react-href-var --config=p/owasp-top-ten __CODE_DIRECTORY__ # This ruleset is intended to produce low false positives, and safe for use in CI/CD pipelines.
$ semgrep --config=p/ci __CODE_DIRECTORY__
Work with a JSON output $ semgrep --config=p/ci __CODE_DIRECTORY__ -o /tmp/semgrep.json --json $ cat /tmp/semgrep.json | jq .results
$ semgrep --config=r/javascript.browser.security.insufficient-postmessage-origin-validation.insufficient-postmessage-origin-validation --config=r/javascript.lang.security.audit.incomplete-sanitization.incomplete-sanitization --config=r/javascript.browser.security.dom-based-xss.dom-based-xss --config=r/typescript.react.security.audit.react-css-injection.react-css-injection --config=r/typescript.react.security.audit.react-href-var.react-href-var --config=p/owasp-top-ten __CODE_DIRECTORY__ # This ruleset is intended to produce low false positives, and safe for use in CI/CD pipelines.
$ semgrep --config=p/ci __CODE_DIRECTORY__
Work with a JSON output $ semgrep --config=p/ci __CODE_DIRECTORY__ -o /tmp/semgrep.json --json $ cat /tmp/semgrep.json | jq .results
Audit with njsscan
Human readable output
$ njsscan __CODE_DIRECTORY__
Work with a JSON output $ njsscan __CODE_DIRECTORY__ -o /tmp/njsscan.json --json $ cat /tmp/njsscan.json | jq .nodejs $ cat /tmp/njsscan.json | jq .templates
Work with a JSON output $ njsscan __CODE_DIRECTORY__ -o /tmp/njsscan.json --json $ cat /tmp/njsscan.json | jq .nodejs $ cat /tmp/njsscan.json | jq .templates
Example
Let's take an example : https://github.com/juice-shop/juice-shop - "OWASP Juice Shop: Probably the most modern and sophisticated insecure web
application"
# Clone the vulnerable repo in /tmp.
$ git clone https://github.com/juice-shop/juice-shop /tmp/juice-shop # Scan it with semgrep, in paranoiac mode.
$ semgrep --config=r/javascript.browser.security.insufficient-postmessage-origin-validation.insufficient-postmessage-origin-validation --config=r/javascript.lang.security.audit.incomplete-sanitization.incomplete-sanitization --config=r/javascript.browser.security.dom-based-xss.dom-based-xss --config=r/typescript.react.security.audit.react-css-injection.react-css-injection --config=r/typescript.react.security.audit.react-href-var.react-href-var --config=p/owasp-top-ten /tmp/juice-shop/ …
/tmp/juice-shop/data/static/codefixes/unionSqlInjectionChallenge_3.ts
javascript.express.security.injection.tainted-sql-string.tainted-sql-string
Detected user input used to manually construct a SQL string. This is usually bad practice because manual construction could accidentally result in a SQL injection. An attacker could use a SQL injection to steal or modify contents of the database. Instead, use a parameterized query which is available by default in most database engines. Alternatively, consider using an object-relational mapper (ORM) such as Sequelize which will protect your queries.
Details: https://sg.run/66ZL
10┆ ... criteria}%' OR description LIKE '%${criteria}%') AND deletedAt IS NULL) ORDER BY name`) ...
[shortened a long line from output, adjust with --max-chars-per-line]
⋮┆----------------------------------------
…
Ran 471 rules on 979 files: 41 findings.
$ git clone https://github.com/juice-shop/juice-shop /tmp/juice-shop # Scan it with semgrep, in paranoiac mode.
$ semgrep --config=r/javascript.browser.security.insufficient-postmessage-origin-validation.insufficient-postmessage-origin-validation --config=r/javascript.lang.security.audit.incomplete-sanitization.incomplete-sanitization --config=r/javascript.browser.security.dom-based-xss.dom-based-xss --config=r/typescript.react.security.audit.react-css-injection.react-css-injection --config=r/typescript.react.security.audit.react-href-var.react-href-var --config=p/owasp-top-ten /tmp/juice-shop/ …
/tmp/juice-shop/data/static/codefixes/unionSqlInjectionChallenge_3.ts
javascript.express.security.injection.tainted-sql-string.tainted-sql-string
Detected user input used to manually construct a SQL string. This is usually bad practice because manual construction could accidentally result in a SQL injection. An attacker could use a SQL injection to steal or modify contents of the database. Instead, use a parameterized query which is available by default in most database engines. Alternatively, consider using an object-relational mapper (ORM) such as Sequelize which will protect your queries.
Details: https://sg.run/66ZL
10┆ ... criteria}%' OR description LIKE '%${criteria}%') AND deletedAt IS NULL) ORDER BY name`) ...
[shortened a long line from output, adjust with --max-chars-per-line]
⋮┆----------------------------------------
…
Ran 471 rules on 979 files: 41 findings.
Does it work on my CI ?
https://semgrep.dev/docs/semgrep-ci/running-semgrep-ci-without-semgrep-app/
There is documentation for:
There is documentation for:
- GitHub Actions
- GitLab CI/CD
- Jenkins
- BitBucket Pipelines
- CircleCI
- Buildkite
Software Composition Analysis (SCA)
Depscan by AppThreat is a fully open-source security audit for project dependencies based on known vulnerabilities and advisories.
cdxgen is extracting all dependencies from npm-shrinkwrap.json, package-lock.json, pnpm-lock.yaml, yarn.lock, rush.js, bower.json, .min.js, and transitive dependencies.
It doesn't work perfectly for all languages, but for JavaScript it works great.
cdxgen is extracting all dependencies from npm-shrinkwrap.json, package-lock.json, pnpm-lock.yaml, yarn.lock, rush.js, bower.json, .min.js, and transitive dependencies.
It doesn't work perfectly for all languages, but for JavaScript it works great.
Install
# Install cdxgen in /tmp/, use -g for a global install instead of --prefix /tmp/
$ npm install --prefix /tmp/ @appthreat/cdxgen # Not very clean, but temporary
$ PATH=$PATH:/tmp/node_modules/.bin $ pip install appthreat-depscan
$ npm install --prefix /tmp/ @appthreat/cdxgen # Not very clean, but temporary
$ PATH=$PATH:/tmp/node_modules/.bin $ pip install appthreat-depscan
Audit
Human readable output
$ cdxgen -o /tmp/bom.json __CODE_DIRECTORY__
$ depscan --bom /tmp/bom.json -o /tmp/depscan.json
Work with a JSON output $ cat /tmp/depscan.json | jq .
Work with a JSON output $ cat /tmp/depscan.json | jq .
Example
In this example, I'm using this random GitHub repository: https://github.com/Faten4/HW-Search
# Clone the vulnerable repo in /tmp.
$ git clone https://github.com/Faten4/HW-Search /tmp/HW-Search $ cdxgen -o /tmp/bom.json /tmp/HW-Search/ $ depscan --bom /tmp/bom.json -o /tmp/depscan.json
$ git clone https://github.com/Faten4/HW-Search /tmp/HW-Search $ cdxgen -o /tmp/bom.json /tmp/HW-Search/ $ depscan --bom /tmp/bom.json -o /tmp/depscan.json
Dependency Scan Results (nodejs)
╔════════════════╤═══════════╤═══════════════════════╤═════════╤═════════════╤══════════╤═══════╗
║ Id │ Package │ Insights │ Version │ Fix Version │ Severity │ Score ║
╟────────────────┼───────────┼───────────────────────┼─────────┼─────────────┼──────────┼───────╢
║ CVE-2021-3803 │ nth-check │ ℹ Indirect dependency │ <2.0.1 │ 2.0.1 │ HIGH │ 7.5 ║
╟────────────────┼───────────┼───────────────────────┼─────────┼─────────────┼──────────┼───────╢
║ CVE-2022-40215 │ tabs │ 🎯 Direct usage │ <=3.7.1 │ │ MEDIUM │ 5.4 ║
╟────────────────┼───────────┼───────────────────────┼─────────┼─────────────┼──────────┼───────╢
║ CVE-2022-33154 │ schema │ ℹ Indirect dependency │ <1.13.1 │ 1.13.1 │ MEDIUM │ 5.4 ║
╚════════════════╧═══════════╧═══════════════════════╧═════════╧═════════════╧══════════╧═══════╝
╔════════════════╤═══════════╤═══════════════════════╤═════════╤═════════════╤══════════╤═══════╗
║ Id │ Package │ Insights │ Version │ Fix Version │ Severity │ Score ║
╟────────────────┼───────────┼───────────────────────┼─────────┼─────────────┼──────────┼───────╢
║ CVE-2021-3803 │ nth-check │ ℹ Indirect dependency │ <2.0.1 │ 2.0.1 │ HIGH │ 7.5 ║
╟────────────────┼───────────┼───────────────────────┼─────────┼─────────────┼──────────┼───────╢
║ CVE-2022-40215 │ tabs │ 🎯 Direct usage │ <=3.7.1 │ │ MEDIUM │ 5.4 ║
╟────────────────┼───────────┼───────────────────────┼─────────┼─────────────┼──────────┼───────╢
║ CVE-2022-33154 │ schema │ ℹ Indirect dependency │ <1.13.1 │ 1.13.1 │ MEDIUM │ 5.4 ║
╚════════════════╧═══════════╧═══════════════════════╧═════════╧═════════════╧══════════╧═══════╝
Does it work on my CI ?
https://github.com/appthreat/dep-scan#integration-with-ci-environments
There is documentation for:
There is documentation for:
- GitHub Actions
Conclusion
You don’t necessarily need an expensive code scanner like Snyk or CherckMarx, you can build it yourself. If you put it in pre-commit hooks, CI/CD and asynchronous you will have a complete view of your
application security risk and earn awareness of the developers about security concerns.
At Tandem Technology, we'll help you improve your development practices during a workshop to harden your code repository.
At Tandem Technology, we'll help you improve your development practices during a workshop to harden your code repository.