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
11 changes: 5 additions & 6 deletions .pre-commit-hooks.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
103 changes: 96 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,100 @@
# 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.

[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
### Configuration
You can configure the pre-commit hook by modifying the parameters in the IDE Extension settings.


## Standalone IDE Pre-commit hooks

<details>
<summary>Click to expand Jit Docker Login Script</summary>

```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="<YOUR_CLIENT_ID>"
SECRET="<YOUR_CLIENT_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
```
</details>

### 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.
24 changes: 24 additions & 0 deletions pre_commit_hooks/gitleaks.py
Original file line number Diff line number Diff line change
@@ -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()
24 changes: 24 additions & 0 deletions pre_commit_hooks/kics.py
Original file line number Diff line number Diff line change
@@ -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()
Empty file.
149 changes: 149 additions & 0 deletions pre_commit_hooks/utils/controls.py
Original file line number Diff line number Diff line change
@@ -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",
]
24 changes: 24 additions & 0 deletions pre_commit_hooks/utils/git.py
Original file line number Diff line number Diff line change
@@ -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
13 changes: 13 additions & 0 deletions pre_commit_hooks/utils/report.py
Original file line number Diff line number Diff line change
@@ -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")
3 changes: 2 additions & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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
Expand Down