Skip to content
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
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ For more details on the design and integration of SAST tools and datasets in Cod
|SpotBugs|Java|✅|✅|✅|[Latest PR](https://github.com/OPPIDA/CodeSecTools/actions/workflows/ci.yaml)|
|Cppcheck|C/C++|✅|✅|✅|[Latest PR](https://github.com/OPPIDA/CodeSecTools/actions/workflows/ci.yaml)|

Languages supported by the SAST tool are also available, but they are not actively maintained (some features are disabled).

## Usage

### Running the Tool
Expand Down
10 changes: 5 additions & 5 deletions codesectools/sasts/all/report/HTML.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,11 @@ class HTMLReport(Report):
<body>
<a href="./home.html"><h1>CodeSecTools All SAST Tools Report</h1></a>
<h3>SAST Tools used: [sasts]</h3>
<h2>[name]</h2>
<h2>[cstools-name]</h2>
<pre style="font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace"><code style="font-family:inherit">{code}</code></pre>
<script src="https://unpkg.com/@popperjs/core@2"></script>
<script src="https://unpkg.com/tippy.js@6"></script>
<script>[tippy_calls]</script>
<script>[cstools-tippy_calls]</script>
<a href="#" id="top">^</a>
</body>
</html>
Expand Down Expand Up @@ -164,12 +164,12 @@ def generate_single_defect(self, defect_file: dict) -> str:
html_content = file_page.export_html(code_format=self.TEMPLATE)
html_content = html_content.replace('href="HACK', 'id="')
html_content = html_content.replace(
"[name]",
"[cstools-name]",
str(
Path(defect_file["source_path"]).relative_to(self.result.source_path) # ty:ignore[no-matching-overload]
),
)
html_content = html_content.replace("[tippy_calls]", tippy_calls)
html_content = html_content.replace("[cstools-tippy_calls]", tippy_calls)

return html_content

Expand Down Expand Up @@ -218,7 +218,7 @@ def generate(self) -> None:
home_page.print(main_table)
html_content = home_page.export_html(code_format=self.TEMPLATE)
html_content = html_content.replace(
"[name]", f"Project: {self.result.source_path}"
"[cstools-name]", f"Project: {self.result.source_path}"
)

report_home_file = self.report_dir / "home.html"
Expand Down
2 changes: 1 addition & 1 deletion codesectools/sasts/all/sast.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ def __init__(self) -> None:
self.sasts_by_dataset = {}

for sast in self.full_sasts:
for lang in sast.supported_languages:
for lang in sast.supported_languages + sast.extra_languages:
if self.sasts_by_lang.get(lang):
self.sasts_by_lang[lang].append(sast)
else:
Expand Down
4 changes: 3 additions & 1 deletion codesectools/sasts/core/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,9 @@ def analyze(
lang: Annotated[
str,
typer.Argument(
click_type=Choice(self.sast.supported_languages),
click_type=Choice(
self.sast.supported_languages + self.sast.extra_languages
),
help="Source code language (only one at the time)",
metavar="LANG",
),
Expand Down
3 changes: 3 additions & 0 deletions codesectools/sasts/core/parser/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from abc import ABC, abstractmethod
from pathlib import Path
from typing import Literal, Self
from urllib.parse import unquote

from codesectools.shared.cwe import CWE

Expand Down Expand Up @@ -52,6 +53,8 @@ def __init__(
lines: A list of line numbers where the defect is located.

"""
# URL decode
filepath = Path(unquote(str(filepath)))
if not filepath.is_file():
raise FileNotFoundError(filepath.resolve())
self.filepath = filepath
Expand Down
3 changes: 3 additions & 0 deletions codesectools/sasts/core/sast/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ class AnalysisInfo(BaseModel):
project_dir: The absolute path to the project directory that was analyzed.
lang: The programming language of the project.
command_lines: The command lines that were executed for the analysis.
logs: The logs generated during the analysis.
duration: The duration of the analysis in seconds.
lines_of_codes: The number of lines of code in the project for the given language.

Expand All @@ -66,6 +67,7 @@ class SAST(ABC):
Attributes:
name (str): The name of the SAST tool.
supported_languages (list[str]): A list of supported programming languages.
extra_languages (list[str]): Languages supported by the tool itself but not codesectools.
supported_dataset_names (list[str]): Names of compatible datasets.
supported_datasets (list[Dataset]): A list of supported dataset classes.
properties (SASTProperties): The properties of the SAST tool.
Expand All @@ -89,6 +91,7 @@ class SAST(ABC):

name: str
supported_languages: list[str]
extra_languages: list[str] = []
supported_dataset_names: list[str]
supported_datasets: list[Dataset]
properties: SASTProperties
Expand Down
2 changes: 2 additions & 0 deletions codesectools/sasts/tools/Bearer/sast.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ class BearerSAST(BuildlessSAST):
Attributes:
name (str): The name of the SAST tool.
supported_languages (list[str]): A list of supported programming languages.
extra_languages (list[str]): Languages supported by the tool itself but not codesectools.
supported_dataset_names (list[str]): A list of names of compatible datasets.
properties (SASTProperties): The properties of the SAST tool.
requirements (SASTRequirements): The requirements for the SAST tool.
Expand All @@ -39,6 +40,7 @@ class BearerSAST(BuildlessSAST):

name = "Bearer"
supported_languages = ["java"]
extra_languages = ["go", "javascript", "php", "python", "ruby"]
supported_dataset_names = ["BenchmarkJava", "CVEfixes"]
properties = SASTProperties(free=True, offline=True)
requirements = SASTRequirements(
Expand Down
15 changes: 15 additions & 0 deletions codesectools/sasts/tools/Coverity/sast.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ class CoveritySAST(BuildlessSAST):
Attributes:
name (str): The name of the SAST tool.
supported_languages (list[str]): A list of supported programming languages.
extra_languages (list[str]): Languages supported by the tool itself but not codesectools.
supported_dataset_names (list[str]): A list of names of compatible datasets.
properties (SASTProperties): The properties of the SAST tool.
requirements (SASTRequirements): The requirements for the SAST tool.
Expand All @@ -33,6 +34,20 @@ class CoveritySAST(BuildlessSAST):

name = "Coverity"
supported_languages = ["c", "java"]
extra_languages = [
"csharp",
"dart",
"go",
"javascript",
"kotlin",
"objective-c",
"php",
"python",
"ruby",
"apex",
"swift",
"typescript",
]
supported_dataset_names = ["BenchmarkJava", "CVEfixes"]
properties = SASTProperties(free=False, offline=True)
requirements = SASTRequirements(
Expand Down
15 changes: 15 additions & 0 deletions codesectools/sasts/tools/SemgrepCE/sast.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ class SemgrepCESAST(BuildlessSAST):
Attributes:
name (str): The name of the SAST tool.
supported_languages (list[str]): A list of supported programming languages.
extra_languages (list[str]): Languages supported by the tool itself but not codesectools.
supported_dataset_names (list[str]): A list of names of compatible datasets.
properties (SASTProperties): The properties of the SAST tool.
requirements (SASTRequirements): The requirements for the SAST tool.
Expand All @@ -38,6 +39,20 @@ class SemgrepCESAST(BuildlessSAST):

name = "SemgrepCE"
supported_languages = ["java", "c"]
extra_languages = [
"csharp",
"go",
"javascript",
"kotlin",
"python",
"typescript",
"jsx",
"ruby",
"scala",
"swift",
"rust",
"php",
]
supported_dataset_names = ["BenchmarkJava", "CVEfixes", "JulietTestSuiteC"]
properties = SASTProperties(free=True, offline=True)
requirements = SASTRequirements(
Expand Down
20 changes: 20 additions & 0 deletions codesectools/sasts/tools/SnykCode/sast.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ class SnykCodeSAST(BuildlessSAST):
Attributes:
name (str): The name of the SAST tool.
supported_languages (list[str]): A list of supported programming languages.
extra_languages (list[str]): Languages supported by the tool itself but not codesectools.
supported_dataset_names (list[str]): A list of names of compatible datasets.
properties (SASTProperties): The properties of the SAST tool.
requirements (SASTRequirements): The requirements for the SAST tool.
Expand All @@ -32,6 +33,25 @@ class SnykCodeSAST(BuildlessSAST):

name = "SnykCode"
supported_languages = ["java", "c"]
extra_languages = [
"apex",
"dart",
"elixir",
"go",
"groovy",
"java",
"kotlin",
"javascript",
"csharp",
"php",
"python",
"ruby",
"rust",
"scala",
"swift",
"objective-c",
"typescript",
]
supported_dataset_names = ["BenchmarkJava", "CVEfixes", "JulietTestSuiteC"]
properties = SASTProperties(free=False, offline=False)
requirements = SASTRequirements(
Expand Down
24 changes: 14 additions & 10 deletions codesectools/shared/cloc.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ def __init__(self, dir: Path, lang: str) -> None:
from git import Repo

self.dir = dir
self.lang = self.cloc_names[lang]
self.lang = self.cloc_names.get(lang)
if shutil.which("cloc"):
self.base_command = ["cloc", ".", "--json"]
else:
Expand Down Expand Up @@ -85,18 +85,22 @@ def get_loc(self) -> int:

Returns:
The number of lines of code, or 0 if the language is not found
in the output.
in the output or -1 if the language is not supported by
CodeSecTools.

Raises:
NonZeroExit: If the cloc command fails.

"""
full_command = self.base_command + [f"--include-lang={self.lang}"]
retcode, out = run_command(full_command, self.dir)
if retcode != 0:
raise NonZeroExit(full_command, out)
json_out = json.loads(out)
if lang_stats := json_out.get(self.lang):
return lang_stats["code"]
if self.lang:
full_command = self.base_command + [f"--include-lang={self.lang}"]
retcode, out = run_command(full_command, self.dir)
if retcode != 0:
raise NonZeroExit(full_command, out)
json_out = json.loads(out)
if lang_stats := json_out.get(self.lang):
return lang_stats["code"]
else:
return 0
else:
return 0
return -1
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "CodeSecTools"
version = "0.16.0"
version = "0.16.1"
description = "A framework for code security that provides abstractions for static analysis tools and datasets to support their integration, testing, and evaluation."
readme = "README.md"
license = "AGPL-3.0-only"
Expand Down
Loading
Loading