Skip to content

Commit

Permalink
Feature/add npm audit (#68)
Browse files Browse the repository at this point in the history
* wip checknpm in cli

* in between

* first working state of 'viur check npm'

* refined npm autofix functionality

* added beauty touch ups

* implemented requested changes

---------

Co-authored-by: Christos <[email protected]>
  • Loading branch information
Grashalmbeisser and Grashalmbeisser authored Nov 28, 2023
1 parent 16659c3 commit 3130e08
Show file tree
Hide file tree
Showing 2 changed files with 114 additions and 16 deletions.
123 changes: 107 additions & 16 deletions src/viur_cli/local.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import json
import click
import os
import shutil
import subprocess
from pprint import pprint

from viur_cli import echo_info, echo_positive
from . import cli, echo_error, get_config, utils
from .install import vi as vi_install

Expand Down Expand Up @@ -159,30 +163,33 @@ def env():

@cli.command()
@click.option('--dev', '-d', is_flag=True, default=False)
def check(dev):
"""Perform security checks for vulnerabilities.
@click.option('--autofix', '-a', is_flag=True, default=False)
@click.argument("action", type=click.Choice(['npm', 'all']))
def check(dev, action, autofix):
"""
Perform security checks for vulnerabilities.
The 'check' command performs security checks for vulnerabilities within your project.
It checks for vulnerabilities in the Pipenv and npm dependencies of your project.
You can choose to include development dependencies by using the
'--dev' option.
:param dev: bool, default: False
Perform checks on development dependencies if set to 'True'.
You can choose to include development dependencies by using the '--dev' option.
Example Usage:
```shell
viur check --dev
```
Args:
dev (bool): Perform checks on development dependencies if set to 'True'.
autofix (bool): Automatically fix npm vulnerabilities if set to 'True'.
action (str): Specify the action to perform ('npm' or 'all').
The 'check' command helps you identify and address security vulnerabilities in your project's dependencies.
:return: None
Usage:
```shell
viur check --dev npm --autofix
```
"""

if do_checks(dev):
utils.echo_info("\U00002714 No vulnerabilities found.")

if action == "npm":
checknpm(autofix)

def do_checks(dev=True):
"""
Expand All @@ -208,18 +215,17 @@ def show_output_if_not(args, check_str):
all_checks_passed = False

if dev:
if show_output_if_not("pipenv check --output minimal --categories develop".split(), "0 vulnerabilities found"):
if show_output_if_not("pipenv check --output minimal --categories develop".split(),
"0 vulnerabilities found"):
all_checks_passed = False

# Check npm vulnerabilities for all npm builds

projectConfig = get_config()
cfg = projectConfig["default"].copy()
if builds_cfg := cfg.get("builds"):
if npm_apps := [k for k, v in builds_cfg.items() if builds_cfg[k]["kind"] == "npm"]:
for name in npm_apps:
path = os.path.join(cfg["sources_folder"], builds_cfg[name]["source"])

if dev:
args = ("npm", "audit", "--prefix", path)
else:
Expand All @@ -229,3 +235,88 @@ def show_output_if_not(args, check_str):
all_checks_passed = False

return all_checks_passed


def checknpm(autofix):
"""
Check for npm vulnerabilities in the project and optionally fix them.
This function runs the "npm audit" command to check for vulnerabilities in the project's dependencies. If any
vulnerabilities are found, the function prompts the user to view the details and optionally run "npm audit fix
--force" to automatically fix the vulnerabilities.
Args:
autofix (bool): A boolean indicating whether to automatically fix vulnerabilities.
Raises:
subprocess.CalledProcessError: If an error occurs while running the "npm audit" or "npm audit fix" commands.
"""
project_config = get_config()
sources_folder = project_config["default"]["sources_folder"]

try:
# Run "npm audit" command and capture the output
result = subprocess.run(
['npm', 'audit', '--json'],
capture_output=True,
cwd=sources_folder,
encoding='utf-8'
)

audit = json.loads(result.stdout)
vulnerabilities = audit["metadata"]["vulnerabilities"]

if vulnerabilities["total"] >= 0:

if not autofix:
print(
f'Npm found {vulnerabilities["total"]} Vulnerabilities \n'
f'info: {vulnerabilities["info"]}\n'
f'low: {vulnerabilities["low"]}\n'
f'moderate: {vulnerabilities["moderate"]}\n'
f'high: {vulnerabilities["high"]}\n'
f'critical: {vulnerabilities["critical"]}\n'
f'total: {vulnerabilities["total"]}\n'
)
show_vulnerabilities = input('Do you want a list of the found Vulnerabilities? (y/N)').strip().lower()

if show_vulnerabilities == 'y':
pprint(audit["vulnerabilities"])

confirm = input('Do you want to run "npm audit fix --force" automatically? (Y/n):').strip().lower()

if autofix or confirm == 'y' or confirm == '':

try:
fix = subprocess.run(
["npm", "audit", "fix", "--force", "--json"],
capture_output=True,
cwd=sources_folder,
encoding="utf-8",
)

fix_output = json.loads(fix.stdout)

echo_info(
f'npm added {fix_output["added"]} packages,\n'
f'audited {fix_output["audited"]} packages,\n'
f'changed {fix_output["changed"]} packages\n'
f'and removed {fix_output["removed"]} packages\n'
)

show_all_fix = input('Do you want the whole report of the npm fixes?(y/N)').lower().strip()
if show_all_fix == 'y':
pprint(fix_output)

except Exception as e:
echo_error(f'{e}')
else:
echo_info(
'Automatic fix not confirmed. To fix vulnerabilities, '
'run "npm audit fix --force" in the ./sources folder.'
)
else:
echo_positive('No vulnerabilities found.')

except Exception as e:
echo_error(f'An unexpected error occurred: {e}')
7 changes: 7 additions & 0 deletions src/viur_cli/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,17 @@ def echo_error(msg):
"""colored cli feedback"""
click.echo(click.style("ERROR: " + msg, fg="red"))


def echo_positive(msg):
"""colored cli feedback"""
click.echo(click.style("INFO: " + msg, fg="green"))


def echo_warning(msg):
"""colored cli feedback"""
click.echo(click.style("WARNING: " + msg, fg=(255, 231, 0)))


def echo_fatal(msg):
echo_error(msg)
sys.exit(1)
Expand Down

0 comments on commit 3130e08

Please sign in to comment.