Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add option to skip def_ws prefix in sarif reports #2383

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 7 additions & 5 deletions docs/reporters/SarifReporter.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
title: SARIF Reporter for MegaLinter
description: Generates SAST results in SARIF format within a file named mega-linter-report.sarif, located in report folder
---

# SARIF Reporter (beta)

Generates a full execution log in SARIF format within a file named **mega-linter-report.sarif** , located in report folder.
Expand Down Expand Up @@ -30,8 +31,9 @@ with:

## Configuration

| Variable | Description | Default value |
|--------------------------|--------------------------------------------------------------------------------------------------------|----------------------------|
| SARIF_REPORTER | Activates/deactivates reporter | `false` |
| SARIF_REPORTER_FILE_NAME | File name for SARIF report output file | `mega-linter-report.sarif` |
| SARIF_REPORTER_LINTERS | List of linter keys that will output SARIF (if not set, all SARIF compliant linters will output SARIF) | `[]` |
| Variable | Description | Default value |
|-----------------------------------------|------------------------------------------------------------------------------------------------------------|----------------------------|
| SARIF_REPORTER | Activates/deactivates reporter | `false` |
| SARIF_REPORTER_NORMALIZE_LINTERS_OUTPUT | Remove DEFAULT_WORKSPACE prefix in SARIF-files, i.e. 'DEFAULT_WORKSPACE/src/main' would be 'src/main' etc. | `true` |
| SARIF_REPORTER_FILE_NAME | File name for SARIF report output file | `mega-linter-report.sarif` |
| SARIF_REPORTER_LINTERS | List of linter keys that will output SARIF (if not set, all SARIF compliant linters will output SARIF) | `[]` |
97 changes: 68 additions & 29 deletions megalinter/Linter.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
from time import perf_counter

import yaml
from megalinter import config, pre_post_factory, utils, utils_reporter
from megalinter import config, pre_post_factory, utils, utils_sarif, utils_reporter
from megalinter.constants import DEFAULT_DOCKER_WORKSPACE_DIR


Expand Down Expand Up @@ -108,7 +108,8 @@ def __init__(self, params=None, linter_config=None):
self.sarif_default_output_file = None
self.no_config_if_fix = False
self.cli_lint_extra_args = [] # Extra arguments to send to cli everytime
self.cli_lint_fix_arg_name = None # Name of the cli argument to send in case of APPLY_FIXES required by user
# Name of the cli argument to send in case of APPLY_FIXES required by user
self.cli_lint_fix_arg_name = None
self.cli_lint_fix_remove_args = (
[]
) # Arguments to remove in case fix argument is sent
Expand Down Expand Up @@ -177,7 +178,8 @@ def __init__(self, params=None, linter_config=None):
)
if self.output_sarif is True:
# Disable SARIF if linter not in specified linter list
sarif_enabled_linters = config.get_list("SARIF_REPORTER_LINTERS", None)
sarif_enabled_linters = config.get_list(
"SARIF_REPORTER_LINTERS", None)
if (
sarif_enabled_linters is not None
and self.name not in sarif_enabled_linters
Expand Down Expand Up @@ -216,7 +218,8 @@ def __init__(self, params=None, linter_config=None):
self.apply_fixes = False
# APPLY_FIXES is "all"
elif param_apply_fixes == "all" or (
isinstance(param_apply_fixes, bool) and param_apply_fixes is True
isinstance(param_apply_fixes,
bool) and param_apply_fixes is True
):
self.apply_fixes = True
# APPLY_FIXES is a comma-separated list in a single string
Expand Down Expand Up @@ -390,7 +393,8 @@ def manage_activation(self, params):
self.is_active = True
# check activation rules
if self.is_active is True and len(self.activation_rules) > 0:
self.is_active = utils.check_activation_rules(self.activation_rules, self)
self.is_active = utils.check_activation_rules(
self.activation_rules, self)

# Manage configuration variables
def load_config_vars(self, params):
Expand All @@ -399,22 +403,26 @@ def load_config_vars(self, params):
if config.exists(self.name + "_CONFIG_FILE"):
self.config_file_name = config.get(self.name + "_CONFIG_FILE")
elif config.exists(self.descriptor_id + "_CONFIG_FILE"):
self.config_file_name = config.get(self.descriptor_id + "_CONFIG_FILE")
self.config_file_name = config.get(
self.descriptor_id + "_CONFIG_FILE")
elif config.exists(self.name + "_FILE_NAME"):
self.config_file_name = config.get(self.name + "_FILE_NAME")
elif config.exists(self.descriptor_id + "_FILE_NAME"):
self.config_file_name = config.get(self.descriptor_id + "_FILE_NAME")
self.config_file_name = config.get(
self.descriptor_id + "_FILE_NAME")
# Ignore file name: try first NAME + _FILE_NAME, then LANGUAGE + _FILE_NAME
if self.cli_lint_ignore_arg_name is not None:
if config.exists(self.name + "_IGNORE_FILE"):
self.ignore_file_name = config.get(self.name + "_IGNORE_FILE")
elif config.exists(self.descriptor_id + "_IGNORE_FILE"):
self.ignore_file_name = config.get(self.descriptor_id + "_IGNORE_FILE")
self.ignore_file_name = config.get(
self.descriptor_id + "_IGNORE_FILE")
# Linter rules path: try first NAME + _RULE_PATH, then LANGUAGE + _RULE_PATH
if config.exists(self.name + "_RULES_PATH"):
self.linter_rules_path = config.get(self.name + "_RULES_PATH")
elif config.exists(self.descriptor_id + "_RULES_PATH"):
self.linter_rules_path = config.get(self.descriptor_id + "_RULES_PATH")
self.linter_rules_path = config.get(
self.descriptor_id + "_RULES_PATH")
# Linter config file:
# 0: LINTER_DEFAULT set in user config: let the linter find it, do not reference it in cli arguments
# 1: http rules path: fetch remove file and copy it locally (then delete it after linting)
Expand Down Expand Up @@ -582,14 +590,16 @@ def load_config_vars(self, params):

# Include regex :try first NAME + _FILTER_REGEX_INCLUDE, then LANGUAGE + _FILTER_REGEX_INCLUDE
if config.exists(self.name + "_FILTER_REGEX_INCLUDE"):
self.filter_regex_include = config.get(self.name + "_FILTER_REGEX_INCLUDE")
self.filter_regex_include = config.get(
self.name + "_FILTER_REGEX_INCLUDE")
elif config.exists(self.descriptor_id + "_FILTER_REGEX_INCLUDE"):
self.filter_regex_include = config.get(
self.descriptor_id + "_FILTER_REGEX_INCLUDE"
)
# User arguments from config
if config.get(self.name + "_ARGUMENTS", "") != "":
self.cli_lint_user_args = config.get_list_args(self.name + "_ARGUMENTS")
self.cli_lint_user_args = config.get_list_args(
self.name + "_ARGUMENTS")

# Get PRE_COMMANDS overridden by user
if config.get(self.name + "_PRE_COMMANDS", "") != "":
Expand Down Expand Up @@ -618,7 +628,8 @@ def load_config_vars(self, params):
self.disable_errors = True
# Exclude regex: try first NAME + _FILTER_REGEX_EXCLUDE, then LANGUAGE + _FILTER_REGEX_EXCLUDE
if config.exists(self.name + "_FILTER_REGEX_EXCLUDE"):
self.filter_regex_exclude = config.get(self.name + "_FILTER_REGEX_EXCLUDE")
self.filter_regex_exclude = config.get(
self.name + "_FILTER_REGEX_EXCLUDE")
elif config.exists(self.descriptor_id + "_FILTER_REGEX_EXCLUDE"):
self.filter_regex_exclude = config.get(
self.descriptor_id + "_FILTER_REGEX_EXCLUDE"
Expand Down Expand Up @@ -671,10 +682,12 @@ def run(self):
self.status = "warning" if self.disable_errors is True else "error"
self.return_code = 0 if self.disable_errors is True else 1
self.number_errors += 1
self.total_number_errors += self.get_total_number_errors(stdout)
self.total_number_errors += self.get_total_number_errors(
stdout)
# Build result for list of files
if self.cli_lint_mode == "list_of_files":
self.update_files_lint_results(self.files, None, None, None, None)
self.update_files_lint_results(
self.files, None, None, None, None)

# Set return code to 0 if failures in this linter must not make the MegaLinter run fail
if self.return_code != 0:
Expand Down Expand Up @@ -709,7 +722,8 @@ def replace_vars(self, variables):
variables_with_replacements = []
for txt in variables:
if "{{SARIF_OUTPUT_FILE}}" in txt:
txt = txt.replace("{{SARIF_OUTPUT_FILE}}", self.sarif_output_file)
txt = txt.replace("{{SARIF_OUTPUT_FILE}}",
self.sarif_output_file)
elif "{{REPORT_FOLDER}}" in txt:
txt = txt.replace("{{REPORT_FOLDER}}", self.report_folder)
elif "{{WORKSPACE}}" in txt:
Expand Down Expand Up @@ -748,7 +762,8 @@ def update_files_lint_results(

# List all reporters, then instantiate each of them
def load_reporters(self):
reporter_init_params = {"master": self, "report_folder": self.report_folder}
reporter_init_params = {"master": self,
"report_folder": self.report_folder}
self.reporters = utils.list_active_reporters_for_scope(
"linter", reporter_init_params
)
Expand Down Expand Up @@ -874,9 +889,19 @@ def manage_sarif_output(self, return_stdout):
if os.path.isfile(self.sarif_default_output_file)
else os.path.join(self.workspace, self.sarif_default_output_file)
)
shutil.move(linter_sarif_report, self.sarif_output_file)
sarif_confirmed = True
logging.debug(f"Moved {linter_sarif_report} to {self.sarif_output_file}")

# Check that a sarif report really exists before moving it etc)
if os.path.isfile(linter_sarif_report):
shutil.move(linter_sarif_report, self.sarif_output_file)
sarif_confirmed = True
logging.debug(
f"Moved {linter_sarif_report} to {self.sarif_output_file}"
)
else:
logging.debug(
f"Could not find {linter_sarif_report} (linter sarif output error?)"
)
sarif_confirmed = False
# Manage case when SARIF output is in stdout (and not generated by the linter)
elif (
self.can_output_sarif is True
Expand All @@ -899,10 +924,15 @@ def manage_sarif_output(self, return_stdout):
and os.path.isfile(self.sarif_output_file)
):
sarif_confirmed = True

if sarif_confirmed is True:
utils_sarif.normalize_sarif_files(self)

# Convert SARIF into human readable text for Console & Text reporters
if sarif_confirmed is True and self.master.sarif_to_human is True:
with open(self.sarif_output_file, "r", encoding="utf-8") as file:
self.stdout_human = utils_reporter.convert_sarif_to_human(file.read())
self.stdout_human = utils_reporter.convert_sarif_to_human(
file.read())

# Returns linter version (can be overridden in special cases, like version has special format)
def get_linter_version(self):
Expand Down Expand Up @@ -933,9 +963,11 @@ def get_linter_version_output(self):
)
return_code = process.returncode
output = utils.decode_utf8(process.stdout)
logging.debug("Linter version result: " + str(return_code) + " " + output)
logging.debug("Linter version result: " +
str(return_code) + " " + output)
except FileNotFoundError:
logging.warning("Unable to call command [" + " ".join(command) + "]")
logging.warning(
"Unable to call command [" + " ".join(command) + "]")
return_code = 666
output = "ERROR"

Expand All @@ -944,7 +976,8 @@ def get_linter_version_output(self):
"Unable to get version for linter [" + self.linter_name + "]"
)
logging.warning(
" ".join(command) + f" returned output: ({str(return_code)}) " + output
" ".join(command) +
f" returned output: ({str(return_code)}) " + output
)
return "ERROR"
else:
Expand Down Expand Up @@ -972,16 +1005,20 @@ def get_linter_help(self):
)
return_code = process.returncode
output += utils.decode_utf8(process.stdout)
logging.debug("Linter help result: " + str(return_code) + " " + output)
logging.debug("Linter help result: " +
str(return_code) + " " + output)
except FileNotFoundError:
logging.warning("Unable to call command [" + " ".join(command) + "]")
logging.warning(
"Unable to call command [" + " ".join(command) + "]")
return_code = 666
output += "ERROR"
break

if return_code != self.help_command_return_code or output.strip() == "":
logging.warning("Unable to get help for linter [" + self.linter_name + "]")
logging.warning(f"{str(command)} returned output: ({return_code}) {output}")
logging.warning(
"Unable to get help for linter [" + self.linter_name + "]")
logging.warning(
f"{str(command)} returned output: ({return_code}) {output}")
return "ERROR"
else:
return output
Expand Down Expand Up @@ -1119,9 +1156,11 @@ def get_ignore_arguments(self, cmd):
self.workspace, DEFAULT_DOCKER_WORKSPACE_DIR
)
if self.cli_lint_ignore_arg_name.endswith("="):
ignore_args += [self.cli_lint_ignore_arg_name + self.final_ignore_file]
ignore_args += [self.cli_lint_ignore_arg_name +
self.final_ignore_file]
elif self.cli_lint_ignore_arg_name != "":
ignore_args += [self.cli_lint_ignore_arg_name, self.final_ignore_file]
ignore_args += [self.cli_lint_ignore_arg_name,
self.final_ignore_file]
return ignore_args

# Manage SARIF arguments
Expand Down
Loading