From c7891bd1f6964ff8e72786720c2892bb1994c3ed Mon Sep 17 00:00:00 2001 From: Guy7B Date: Thu, 12 Oct 2023 16:28:44 +0300 Subject: [PATCH 1/2] =?UTF-8?q?=E2=9C=A8=20Standalone=20Jit=20Pre-Commit?= =?UTF-8?q?=20Hook?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .pre-commit-hooks.yaml | 11 +-- README.md | 47 +++++++-- pre_commit_hooks/gitleaks.py | 24 +++++ pre_commit_hooks/kics.py | 24 +++++ pre_commit_hooks/utils/__init__.py | 0 pre_commit_hooks/utils/controls.py | 149 +++++++++++++++++++++++++++++ pre_commit_hooks/utils/git.py | 24 +++++ pre_commit_hooks/utils/report.py | 13 +++ setup.cfg | 3 +- 9 files changed, 281 insertions(+), 14 deletions(-) create mode 100644 pre_commit_hooks/gitleaks.py create mode 100644 pre_commit_hooks/kics.py create mode 100644 pre_commit_hooks/utils/__init__.py create mode 100644 pre_commit_hooks/utils/controls.py create mode 100644 pre_commit_hooks/utils/git.py create mode 100644 pre_commit_hooks/utils/report.py diff --git a/.pre-commit-hooks.yaml b/.pre-commit-hooks.yaml index 98ba772..0e10800 100644 --- a/.pre-commit-hooks.yaml +++ b/.pre-commit-hooks.yaml @@ -3,9 +3,8 @@ description: Checks if there are new security issues that Jit IDE Extension found entry: vscode language: python -# - id: kics -# name: run kics -# description: run kics on the current commit -# entry: kics -# language: python -# files: (\.tf)$ +- id: gitleaks + name: Run GitLeaks + description: Detect secrets on the current commit + entry: gitleaks + language: python diff --git a/README.md b/README.md index ee06756..1659a4e 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,44 @@ -# Jit IDE Extension pre-commit hooks -A set of pre-commit hooks to use along with the IDE Extension. +# Jit - IDE pre-commit hooks -## Getting Started -Instructions for how to set up and use your pre-commit-hooks. This should include any dependencies that need to be installed, and any configuration steps that need to be performed. -## Usage -Once the pre-commit hook is installed and configured, it will automatically run whenever you try to commit changes to your repository. If there are findings in the specified file, the commit will be blocked and you will need to address the findings before committing again. +## Jit IDE Extension (VSCode) pre-commit hook +A pre-commit hook to use along with the IDE Extension. -## Configuration +[Getting Started with the pre-commit hook for Jit VSCode IDE Extension users](https://docs.jit.io/docs/jit-ide-extension-for-visual-studio#pre-commit-hook) + +### Configuration You can configure the pre-commit hook by modifying the parameters in the IDE Extension settings. + + +## Standalone IDE Pre-commit hooks + + +### Secrets Detection +A pre-commit hook to detect secrets in your code. This hook will scan the files you specify for secrets and block the commit if any are found. + +### Getting Started +1. **Install `pre-commit`:** + ```bash + pip install pre-commit + ``` + +2. **Create a `.pre-commit-config.yaml` in your repo:** + ```yaml + repos: + - repo: https://github.com/jitsecurity/ide-pre-commit-hooks + rev: 0.1.0 + hooks: + - id: gitleaks + ``` + +3. **Install the hook:** + ```bash + pre-commit install + ``` + +4. **Commit changes:** + When you commit, the `pre-commit` hook will automatically run. + + +## Usage +Once the pre-commit hook is installed and configured, it will automatically run whenever you try to commit changes to your repository. If there are findings in the specified file, the commit will be blocked and you will need to address the findings before committing again. diff --git a/pre_commit_hooks/gitleaks.py b/pre_commit_hooks/gitleaks.py new file mode 100644 index 0000000..15f2e7f --- /dev/null +++ b/pre_commit_hooks/gitleaks.py @@ -0,0 +1,24 @@ +import argparse +import os +import sys + +from pre_commit_hooks.utils.controls import GitLeaks +from pre_commit_hooks.utils.report import print_report + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument("filenames", nargs="*", help="Filenames to fix") + args = parser.parse_args(sys.argv) + + c = GitLeaks(os.getcwd(), args.filenames) + c.run_container() + findings = c.get_relevant_findings() + if findings: + print_report(findings, "Secrets") + exit(1) + exit(0) + + +if __name__ == "__main__": + main() diff --git a/pre_commit_hooks/kics.py b/pre_commit_hooks/kics.py new file mode 100644 index 0000000..9be55d0 --- /dev/null +++ b/pre_commit_hooks/kics.py @@ -0,0 +1,24 @@ +import argparse +import os +import sys + +from pre_commit_hooks.utils.controls import Kics +from pre_commit_hooks.utils.report import print_report + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument("filenames", nargs="*", help="Filenames to fix") + args = parser.parse_args(sys.argv) + + c = Kics(os.getcwd(), args.filenames) + c.run_container() + findings = c.get_relevant_findings() + if findings: + print_report(findings, "Infrastructure as Code") + exit(1) + exit(0) + + +if __name__ == "__main__": + main() diff --git a/pre_commit_hooks/utils/__init__.py b/pre_commit_hooks/utils/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pre_commit_hooks/utils/controls.py b/pre_commit_hooks/utils/controls.py new file mode 100644 index 0000000..69ab393 --- /dev/null +++ b/pre_commit_hooks/utils/controls.py @@ -0,0 +1,149 @@ +import json +import os +import subprocess +from typing import List + +from pre_commit_hooks.utils.git import get_git_ignored_files, get_git_root_folders + + +class Control: + image = "" + command = [] + control_name = "" + base_findings_path = "/tmp/controls/.pre_commit_findings" + base_findings_path_raw = "/tmp/controls/.raw_pre_commit_findings" + security_control_output_file = "" + + def __init__(self, base_path, changed_files: List[str]): + self.base_path = base_path + self.changed_files = changed_files + + @property + def raw_findings_path(self) -> str: + return f"{self.base_findings_path_raw}/{self.control_name}.json" + + @property + def relevant_findings_path(self): + return f"{self.base_findings_path}/{self.control_name}.json" + + @property + def environment(self): + env_vars = { + "FINDINGS_OUTPUT_FILE_PATH": self.raw_findings_path, + } + if self.security_control_output_file: + env_vars["SECURITY_CONTROL_OUTPUT_FILE"] = self.security_control_output_file + return env_vars + + def read_raw_findings_file(self) -> List[dict]: + f = json.loads(open(self.raw_findings_path, "r").read()) + return f + + def create_findings_dir(self) -> None: + os.makedirs(self.base_findings_path_raw, exist_ok=True) + os.makedirs(self.base_findings_path, exist_ok=True) + + @property + def container_params(self) -> dict: + ignored_files_volumes = [ + f"/dev/null:/code/{ignored_file}" + for ignored_file in get_git_ignored_files(self.base_path) + ] + ignored_folders_volumes = [ + f"/code/{ignored_file}" + for ignored_file in get_git_root_folders(self.base_path) + ] + return { + "image": self.image, + "command": self.command, + "volumes": [ + f"{self.base_path.strip()}:/code", + "/tmp/controls:/tmp/controls", + *ignored_files_volumes, + *ignored_folders_volumes, + ], + "environment": self.environment, + "remove": True, + } + + def parse_findings(self, findings): + response = [] + for finding in findings: + finding["filePath"] = os.path.join(self.base_path, finding["filename"]) + finding["filename"] = os.path.basename(finding["filePath"]) + response.append(finding) + return response + + def get_relevant_findings(self) -> List[dict]: + findings = self.read_raw_findings_file() + findings = [f for f in findings if f["filename"] in self.changed_files] + open(self.relevant_findings_path, "w").write( + json.dumps(self.parse_findings(findings)) + ) + return findings + + def run_container(self) -> None: + self.create_findings_dir() + volumes_list = [] + for volume in self.container_params["volumes"]: + volumes_list.extend(["-v", volume]) + command = ( + ["docker", "run"] + + [f"-e {k}={v}" for k, v in self.container_params["environment"].items()] + + volumes_list + + [self.image, *self.command] + ) + process = subprocess.Popen( + command, stdout=subprocess.PIPE, stderr=subprocess.PIPE + ) + stdout, stderr = process.communicate() + with open("/tmp/controls/log", "w") as f: + f.write( + f'Stdout: {stdout.decode("utf-8")}\nStderr{stderr.decode("utf-8")}\n' + ) + + +class GitLeaks(Control): + control_name = "gitleaks" + # image = "ghcr.io/jit-hackathon-secured-ide/jit-gitleaks-control" + image = "899025839375.dkr.ecr.us-east-1.amazonaws.com/jit-ide:jit-gitleaks-control" + # image = "registry.jit.io/jit-ide:jit-gitleaks-control" + + security_control_output_file = "/tmp/controls/report.json" + command = [ + "--", + "detect", + "--config", + "/config/gitleaks.toml", + "--source", + "/code", + "-v", + "--report-format", + "json", + "--report-path", + "/tmp/controls/report.json", + "--redact", + "--no-git", + "--exit-code", + "0", + ] + + +class Kics(Control): + control_name = "kics" + # image = "ghcr.io/jit-hackathon-secured-ide/jit-kics-control:latest" + image = "899025839375.dkr.ecr.us-east-1.amazonaws.com/jit-ide:jit-kics-control" + security_control_output_file = "/tmp/controls/kics/jit-report/results.json" + command = [ + "--", + "scan", + "-p", + "/code", + "-o", + "/tmp/controls/kics/jit-report", + "-f", + "json", + "--exclude-severities", + "INFO,MEDIUM,LOW", + "--disable-secrets", + ] diff --git a/pre_commit_hooks/utils/git.py b/pre_commit_hooks/utils/git.py new file mode 100644 index 0000000..b1e4fe8 --- /dev/null +++ b/pre_commit_hooks/utils/git.py @@ -0,0 +1,24 @@ +import os + + +def get_git_root_folders(git_repo_folder: str) -> list: + gitignore_file = os.path.join(git_repo_folder, ".gitignore") + with open(gitignore_file, "r") as f: + ignored_files = f.read().splitlines() + ignored_folders = [] + for ignored_file in ignored_files: + if os.path.isdir(os.path.join(git_repo_folder, ignored_file)): + ignored_folders.append(ignored_file) + return ignored_folders + + +def get_git_ignored_files(git_repo_folder: str) -> list: + gitignore_file = os.path.join(git_repo_folder, ".gitignore") + with open(gitignore_file, "r") as f: + ignored_files = f.read().splitlines() + ignored_files = [ + file + for file in ignored_files + if os.path.isfile(os.path.join(git_repo_folder, file)) + ] + return ignored_files diff --git a/pre_commit_hooks/utils/report.py b/pre_commit_hooks/utils/report.py new file mode 100644 index 0000000..a20267a --- /dev/null +++ b/pre_commit_hooks/utils/report.py @@ -0,0 +1,13 @@ +def print_report(findings, type): + print(f"\nšŸ” Security Findings Report ({type}) šŸ”\n") + print("-" * 80) + + for finding in findings: + print(f"\nšŸ“ File: \033[1m{finding['filename']}\033[0m") + print(f"šŸ“ Line: \033[1m{finding['line_range']}\033[0m") + print(f"🚨 Severity: \033[1m{finding['issue_severity']}\033[0m") + print(f"šŸ’” Issue: \033[1m{finding['issue_text']}\033[0m") + + print("-" * 80) + + print(f"\nEnd of {type} report.\n") diff --git a/setup.cfg b/setup.cfg index 996aa74..5dd68e3 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = jit_ide_pre_commit_hooks -version = 0.0.3 +version = 0.1.0 url = https://github.com/jitsecurity/ide-pre-commit-hooks license = MIT license_file = LICENSE @@ -11,6 +11,7 @@ python_requires = >=3.7 [options.entry_points] console_scripts = vscode = pre_commit_hooks.vscode:main + gitleaks = pre_commit_hooks.gitleaks:main [bdist_wheel] universal = True From d20b2eb62bd0c26b59e58d35e276ff6137e5f3a7 Mon Sep 17 00:00:00 2001 From: Guy7B Date: Thu, 12 Oct 2023 18:18:36 +0300 Subject: [PATCH 2/2] =?UTF-8?q?=F0=9F=93=9D=20docs:=20authenticate=20Jit?= =?UTF-8?q?=20docker=20registry?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/README.md b/README.md index 1659a4e..e557f18 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,62 @@ You can configure the pre-commit hook by modifying the parameters in the IDE Ext ## Standalone IDE Pre-commit hooks +
+ Click to expand Jit Docker Login Script + +```bash + #!/bin/bash + + # Endpoint and credentials + LOGIN_ENDPOINT="https://api.jit.io/authentication/login" + REGISTRY_ENDPOINT="https://api.jit.io/ide/registry/authenticate" + # Credentials from Jit Platform + CLIENT_ID="" + SECRET="" + + # Authenticate and retrieve the access token + response=$(curl --silent --location "$LOGIN_ENDPOINT" \ + --header 'Content-Type: application/json' \ + --data "{ + \"clientId\": \"$CLIENT_ID\", + \"secret\": \"$SECRET\" + }") + + # Parse the access token using jq + accessToken=$(echo "$response" | jq -r '.accessToken') + + # Use the access token to make a POST request to /registry/authenticate + registry_response=$(curl --silent --location --request POST "$REGISTRY_ENDPOINT" \ + --header "Authorization: Bearer $accessToken") + + # Extract necessary information for Docker login using jq + username=$(echo "$registry_response" | jq -r '.username') + password=$(echo "$registry_response" | jq -r '.password') + registry_url=$(echo "$registry_response" | jq -r '.registry_url') + + # Perform Docker login + echo "$password" | docker login --username "$username" --password-stdin "$registry_url" + + # Check if Docker login was successful + if [ $? -eq 0 ]; then + echo "Successfully logged in to the Docker registry." + else + echo "Docker login failed." + exit 1 + fi + + # Pull the jit-gitleaks-control image + docker_image="$registry_url:jit-gitleaks-control" + docker pull "$docker_image" + + # Check if Docker pull was successful + if [ $? -eq 0 ]; then + echo "Successfully pulled the image: $docker_image" + else + echo "Failed to pull the image: $docker_image" + fi +``` +
### Secrets Detection A pre-commit hook to detect secrets in your code. This hook will scan the files you specify for secrets and block the commit if any are found.