Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: ajinabraham/njsscan
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 0.3.3
Choose a base ref
...
head repository: ajinabraham/njsscan
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: master
Choose a head ref
  • 19 commits
  • 18 files changed
  • 5 contributors

Commits on Oct 5, 2022

  1. 0.3.4

    ajinabraham committed Oct 5, 2022
    Copy the full SHA
    996bb86 View commit details
  2. Update python_test.yml

    ajinabraham authored Oct 5, 2022
    Copy the full SHA
    0f1e3ba View commit details
  3. sarif fix

    ajinabraham committed Oct 5, 2022
    Copy the full SHA
    f005a35 View commit details

Commits on Dec 16, 2022

  1. Copy the full SHA
    e7a0a61 View commit details

Commits on Jul 27, 2023

  1. Update README.md

    ajinabraham authored Jul 27, 2023
    Copy the full SHA
    54ae7a3 View commit details

Commits on Aug 29, 2023

  1. bump njsscan (#111)

    ajinabraham authored Aug 29, 2023
    Copy the full SHA
    c0cd0fd View commit details

Commits on Aug 30, 2023

  1. Copy the full SHA
    5cbf144 View commit details

Commits on Mar 13, 2024

  1. Update timing_attack_node.yaml (#113)

    Add more explanation about the attack. 
    Remove Snyk reference. 
    Add NodeJs reference.
    sebasrevuelta authored Mar 13, 2024
    Copy the full SHA
    40ff09e View commit details

Commits on Apr 5, 2024

  1. bump version + lint qa (#117)

    ajinabraham authored Apr 5, 2024
    Copy the full SHA
    e255017 View commit details
  2. Update nosql_find_injection.yaml to exclude sequelize's .findOne() fa…

    …lse positives (#115)
    bleow authored Apr 5, 2024
    Copy the full SHA
    370e904 View commit details

Commits on Oct 23, 2024

  1. Update open_redirect.yaml (#119)

    Update the description for the rule: express_open_redirect
    sebasrevuelta authored Oct 23, 2024
    Copy the full SHA
    d231083 View commit details
  2. Update xss_templates.yaml (#120)

    * Update xss_templates.yaml
    
    Update description for rule handlebars_safestring
    
    * Update xss_templates.yaml
    sebasrevuelta authored Oct 23, 2024
    Copy the full SHA
    337d0be View commit details

Commits on Nov 4, 2024

  1. Bump semgrep to 1.86.0 (#121)

    * Bump semgrep to 1.86.0
    
    * fix test
    
    * update readme
    
    * codeql version
    ajinabraham authored Nov 4, 2024
    Copy the full SHA
    a6fa4c3 View commit details

Commits on Nov 5, 2024

  1. sarif qa (#122)

    ajinabraham authored Nov 5, 2024
    Copy the full SHA
    b207c4b View commit details
  2. Sarif update (#123)

    * sarif qa
    
    * multiple locations in sarif
    ajinabraham authored Nov 5, 2024
    Copy the full SHA
    2815fbd View commit details

Commits on Nov 7, 2024

  1. Update description 3 rules: regex_injection_dos, generic_header_injec…

    …tion and generic_path_traversal (#124)
    
    Updating description for these three rules:
    
    regex_injection_dos
    generic_header_injection
    generic_path_traversal
    sebasrevuelta authored Nov 7, 2024
    Copy the full SHA
    2302c53 View commit details

Commits on Nov 11, 2024

  1. Fix sarif

    ajinabraham committed Nov 11, 2024
    Copy the full SHA
    7b41dcc View commit details
  2. Update README.md

    ajinabraham authored Nov 11, 2024
    Copy the full SHA
    6982d47 View commit details

Commits on Nov 14, 2024

  1. Explicit semgrep install (#126)

    ajinabraham authored Nov 14, 2024
    Copy the full SHA
    83a95fd View commit details
2 changes: 1 addition & 1 deletion .github/workflows/python_test.yml
Original file line number Diff line number Diff line change
@@ -14,7 +14,7 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest, macos-latest]
python-version: [3.7, 3.8, 3.9, '3.10']
python-version: [3.8, 3.9, '3.10']

steps:
- uses: actions/checkout@v2
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM python:3.9-slim-buster
FROM python:3.12-slim

RUN apt-get update \
&& apt-get install gcc -y \
17 changes: 10 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -6,10 +6,7 @@ Made with ![Love](https://cloud.githubusercontent.com/assets/4301109/16754758/82
[![PyPI version](https://badge.fury.io/py/njsscan.svg)](https://badge.fury.io/py/njsscan)
[![platform](https://img.shields.io/badge/platform-osx%2Flinux-green.svg)](https://github.com/ajinabraham/njsscan)
[![License](https://img.shields.io/:license-lgpl3+-blue.svg)](https://www.gnu.org/licenses/lgpl-3.0.en.html)
[![python](https://img.shields.io/badge/python-7+-blue.svg)](https://www.python.org/downloads/)

[![Language grade: Python](https://img.shields.io/lgtm/grade/python/g/ajinabraham/njsscan.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/ajinabraham/njsscan/context:python)
[![Requirements Status](https://requires.io/github/ajinabraham/njsscan/requirements.svg?branch=master)](https://requires.io/github/ajinabraham/njsscan/requirements/?branch=master)
[![python](https://img.shields.io/badge/python-3.7+-blue.svg)](https://www.python.org/downloads/)
[![Build](https://github.com/ajinabraham/njsscan/workflows/Build/badge.svg)](https://github.com/ajinabraham/njsscan/actions?query=workflow%3ABuild)

### Support njsscan
@@ -188,7 +185,10 @@ jobs:
name: njsscan check
steps:
- name: Checkout the code
uses: actions/checkout@v2
uses: actions/checkout@v4.2.2
- uses: actions/setup-python@v5.3.0
with:
python-version: '3.12'
- name: nodejsscan scan
id: njsscan
uses: ajinabraham/njsscan-action@master
@@ -214,14 +214,17 @@ jobs:
name: njsscan code scanning
steps:
- name: Checkout the code
uses: actions/checkout@v2
uses: actions/checkout@v4.2.2
- uses: actions/setup-python@v5.3.0
with:
python-version: '3.12'
- name: nodejsscan scan
id: njsscan
uses: ajinabraham/njsscan-action@master
with:
args: '. --sarif --output results.sarif || true'
- name: Upload njsscan report
uses: github/codeql-action/upload-sarif@v1
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: results.sarif
```
2 changes: 1 addition & 1 deletion njsscan/__init__.py
Original file line number Diff line number Diff line change
@@ -6,7 +6,7 @@
__title__ = 'njsscan'
__authors__ = 'Ajin Abraham'
__copyright__ = f'Copyright {datetime.now().year} Ajin Abraham, OpenSecurity'
__version__ = '0.3.3'
__version__ = '0.4.3'
__version_info__ = tuple(int(i) for i in __version__.split('.'))
__all__ = [
'__title__',
4 changes: 2 additions & 2 deletions njsscan/__main__.py
Original file line number Diff line number Diff line change
@@ -8,7 +8,7 @@
from njsscan.njsscan import NJSScan
from njsscan.formatters import (
cli,
json,
json_out,
sarif,
sonarqube,
)
@@ -81,7 +81,7 @@ def main():
scan_results,
__version__)
elif args.json:
json.json_output(
json_out.json_output(
args.output,
scan_results,
__version__)
File renamed without changes.
169 changes: 73 additions & 96 deletions njsscan/formatters/sarif.py
Original file line number Diff line number Diff line change
@@ -1,122 +1,104 @@
# -*- coding: utf_8 -*-
"""Sarif output format.
"""SARIF output formatter for NodeJS scan results.
Based on https://github.com/microsoft/bandit-sarif-formatter/
blob/master/bandit_sarif_formatter/formatter.py
Based on https://github.com/microsoft/
bandit-sarif-formatter/blob/master/
bandit_sarif_formatter/formatter.py
MIT License, Copyright (c) Microsoft Corporation.
Copyright (c) Microsoft. All Rights Reserved.
MIT License
Copyright (c) Microsoft Corporation.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE
"""
from datetime import datetime
from datetime import datetime, timezone
from pathlib import PurePath
import urllib.parse as urlparse

import sarif_om as om

from jschema_to_python.to_json import to_json


TS_FORMAT = '%Y-%m-%dT%H:%M:%SZ'


def level_from_severity(severity):
if severity == 'ERROR':
return 'error'
elif severity == 'WARNING':
return 'warning'
elif severity == 'INFO':
return 'note'
else:
return 'none'
return {
'ERROR': 'error',
'WARNING': 'warning',
'INFO': 'note',
}.get(severity, 'none')


def to_uri(file_path):
pure_path = PurePath(file_path)
if pure_path.is_absolute():
return pure_path.as_uri()
else:
posix_path = pure_path.as_posix() # Replace backslashes with slashes.
return urlparse.quote(posix_path) # %-encode special characters.
return urlparse.quote(pure_path.as_posix())


def get_rule_name(rule_id):
normalized = []
noms = rule_id.split('_')
for nom in noms:
normalized.append(nom.capitalize())
return ''.join(normalized)
def format_rule_name(rule_id):
return ''.join(word.capitalize() for word in rule_id.split('_'))


def add_results(scan_results, run):
if run.results is None:
run.results = []
res = {}
res.update(scan_results.get('nodejs', []))
res.update(scan_results.get('templates', []))
combined_results = {
**scan_results.get('nodejs', {}),
**scan_results.get('templates', {})}
rules = {}
rule_indices = {}

for rule_id, issue_dict in res.items():
for rule_id, issue_dict in combined_results.items():
if 'files' not in issue_dict:
# no missing controls in sarif
continue
result = create_result(rule_id, issue_dict, rules, rule_indices)
run.results.append(result)

if len(rules) > 0:
result = create_rule_results(
rule_id,
issue_dict,
rules,
rule_indices)
run.results.extend(result)

if rules:
run.tool.driver.rules = list(rules.values())


def create_result(rule_id, issue_dict, rules, rule_indices):
if rule_id in rules:
rule = rules[rule_id]
rule_index = rule_indices[rule_id]
else:
doc = 'https://ajinabraham.github.io/nodejsscan/#{}'.format(rule_id)
def create_rule_results(rule_id, issue_dict, rules, rule_indices):
rule_results = []

rule, rule_index = rules.get(rule_id), rule_indices.get(rule_id)

if not rule:
doc = ('https://ajinabraham.'
f'github.io/nodejsscan/#{rule_id}')
cwe_id = issue_dict['metadata']['cwe'].split(':')[0].lower()
rule = om.ReportingDescriptor(
id=rule_id,
name=get_rule_name(rule_id),
name=format_rule_name(rule_id),
help_uri=doc,
)
properties={'tags': ['security', f'external/cwe/{cwe_id}']})
rule_index = len(rules)
rules[rule_id] = rule
rule_indices[rule_id] = rule_index

locations = []
for item in issue_dict['files']:
physical_location = om.PhysicalLocation(
artifact_location=om.ArtifactLocation(
uri=to_uri(item['file_path'])),
)
physical_location.region = om.Region(
start_line=item['match_lines'][0],
end_line=item['match_lines'][1],
start_column=item['match_position'][0],
end_column=item['match_position'][1],
snippet=om.ArtifactContent(text=item['match_string']),
)
locations.append(om.Location(physical_location=physical_location))
for item in issue_dict.get('files', []):
location = create_location(item)
rule_results.append(create_result(rule, rule_index, issue_dict, [location]))

return rule_results


def create_location(item):
return om.Location(
physical_location=om.PhysicalLocation(
artifact_location=om.ArtifactLocation(uri=to_uri(item['file_path'])),
region=om.Region(
start_line=item['match_lines'][0],
end_line=item['match_lines'][1],
start_column=item['match_position'][0],
end_column=item['match_position'][1],
snippet=om.ArtifactContent(text=item['match_string']))))


def create_result(rule, rule_index, issue_dict, locations):
return om.Result(
rule_id=rule.id,
rule_index=rule_index,
@@ -125,39 +107,34 @@ def create_result(rule_id, issue_dict, rules, rule_indices):
locations=locations,
properties={
'owasp-web': issue_dict['metadata']['owasp-web'],
'cwe': issue_dict['metadata']['cwe'],
},
)
'cwe': issue_dict['metadata']['cwe']})


def sarif_output(outfile, scan_results, njsscan_version):
log = om.SarifLog(
schema_uri=('https://raw.githubusercontent.com/oasis-tcs/'
'sarif-spec/master/Schemata/sarif-schema-2.1.0.json'),
schema_uri=('https://raw.githubusercontent.com/'
'oasis-tcs/sarif-spec/master/Schemata/'
'sarif-schema-2.1.0.json'),
version='2.1.0',
runs=[
om.Run(
tool=om.Tool(driver=om.ToolComponent(
name='nodejsscan',
information_uri='https://github.com/ajinabraham/njsscan',
semantic_version=njsscan_version,
version=njsscan_version),
),
invocations=[
om.Invocation(
end_time_utc=datetime.utcnow().strftime(TS_FORMAT),
execution_successful=True,
),
],
),
],
)
runs=[om.Run(
tool=om.Tool(driver=om.ToolComponent(
name='nodejsscan',
information_uri='https://github.com/ajinabraham/njsscan',
semantic_version=njsscan_version,
version=njsscan_version,
)),
invocations=[om.Invocation(
end_time_utc=datetime.now(timezone.utc).strftime(TS_FORMAT),
execution_successful=True,
)])])
run = log.runs[0]
add_results(scan_results, run)
json_out = to_json(log)

if outfile:
with open(outfile, 'w') as of:
of.write(json_out)
else:
print(json_out)

return json_out
2 changes: 1 addition & 1 deletion njsscan/formatters/sonarqube.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# -*- coding: utf_8 -*-
"""Sonarqube output format."""

from njsscan.formatters.json import json_output
from njsscan.formatters.json_out import json_output


def get_sonarqube_issue(njsscan_issue):
3 changes: 2 additions & 1 deletion njsscan/rules/semantic_grep/crypto/timing_attack_node.yaml
Original file line number Diff line number Diff line change
@@ -485,7 +485,8 @@ rules:
return api != $X;
message: >-
String comparisons using '===', '!==', '!=' and '==' is vulnerable to timing attacks.
More info: https://snyk.io/blog/node-js-timing-attack-ccc-ctf/
A timing attack allows the attacker to learn potentially sensitive information by, for example, measuring how long it takes for the application to respond to a request.
More info: https://nodejs.org/en/learn/getting-started/security-best-practices#information-exposure-through-timing-attacks-cwe-208
languages:
- javascript
severity: WARNING
10 changes: 10 additions & 0 deletions njsscan/rules/semantic_grep/database/nosql_find_injection.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
rules:
- id: node_nosqli_injection
patterns:
- pattern-not-inside: |
$SEQUELIZE = require('sequelize')
...
$SEQUELIZE(...)
...
- pattern-not-inside: |
import $SEQUELIZE from 'sequelize'
...
$SEQUELIZE(...)
...
- pattern-not-inside: |
$SANITIZE = require('mongo-sanitize')
...
8 changes: 5 additions & 3 deletions njsscan/rules/semantic_grep/dos/regex_injection.yaml
Original file line number Diff line number Diff line change
@@ -57,11 +57,13 @@ rules:
- pattern: |
$STR.split(<... $REQ.$PARAM.$BAR ...>)
message: >-
User controlled data in RegExp() can make the application vulnerable to
layer 7 DoS.
User controlled data in RegExp() can make the application vulnerable to layer 7 DoS.
If user input is used to create a regular expression without validation, it can be exploited to create a complex regular expression that takes an excessive amount of time to evaluate. This can lead to a Denial of Service (DoS) attack where the application becomes unresponsive.
Even if a ReDoS attack is not intended, poorly crafted or complex regular expressions from user input can cause performance issues that impact the responsiveness of an application.
Always sanitize and validate user input to ensure that only safe, expected characters are used in the pattern. This can be done by whitelisting known safe characters and escaping potentially harmful ones.
languages:
- javascript
severity: ERROR
metadata:
owasp-web: a1
cwe: cwe-400
cwe: cwe-400
11 changes: 8 additions & 3 deletions njsscan/rules/semantic_grep/headers/header_injection.yaml
Original file line number Diff line number Diff line change
@@ -45,11 +45,16 @@ rules:
- pattern: |
$RES.writeHead(..., { $X: <... $REQ.$QUERY.$FOO ...> }, ...)
message: >-
Untrusted user input in response header will result in HTTP Header
Injection or Response Splitting Attacks.
If user input is not properly sanitized, an attacker can insert malicious data into response headers.
This can lead to HTTP response splitting, where an attacker injects additional headers or even full HTTP responses,
potentially altering how clients or intermediaries (e.g., proxies) handle the request.
This can lead to vulnerabilities like Cross-Site Scripting (XSS) and cache poisoning.
Always sanitize and validate user inputs to ensure they do not contain characters or data that could alter the header structure (e.g., newline characters, control characters).
Another good option is to leverage well-established libraries or frameworks that handle headers securely.
Many frameworks offer built-in methods for setting headers that ensure they are correctly formatted and safe.
languages:
- javascript
severity: ERROR
metadata:
owasp-web: a1
cwe: cwe-644
cwe: cwe-644
Loading