Skip to content
Draft
74 changes: 74 additions & 0 deletions .husky/hooks/code-quality.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
#!/usr/bin/env bash
"""
Code quality checking hook for Husky-style pre-commit setup.
This hook runs Black, isort, flake8, and Bandit for code quality.
"""

echo "🐍 Running Python code quality checks..."

# Function to check if a command exists
command_exists() {
command -v "$1" >/dev/null 2>&1
}

# Check Black formatting
echo " - Checking code formatting with Black..."
if command_exists black; then
if black --check --diff .; then
echo " ✅ Black formatting check passed"
else
echo " ❌ Black formatting issues found"
echo " 💡 Run 'black .' to fix formatting"
exit 1
fi
else
echo " ⚠️ Black not installed, skipping formatting check"
echo " 💡 Install with: pip install black"
fi

# Check isort import sorting
echo " - Checking import sorting with isort..."
if command_exists isort; then
if isort --check-only --diff .; then
echo " ✅ isort import sorting check passed"
else
echo " ❌ isort import sorting issues found"
echo " 💡 Run 'isort .' to fix import sorting"
exit 1
fi
else
echo " ⚠️ isort not installed, skipping import sorting check"
echo " 💡 Install with: pip install isort"
fi

# Check flake8 linting
echo " - Running linting with flake8..."
if command_exists flake8; then
if flake8 --max-line-length=88 --extend-ignore=E203,W503 .; then
echo " ✅ flake8 linting check passed"
else
echo " ❌ flake8 linting issues found"
echo " 💡 Fix the linting issues above"
exit 1
fi
else
echo " ⚠️ flake8 not installed, skipping linting check"
echo " 💡 Install with: pip install flake8"
fi

# Check Bandit security linting
echo " - Running security linting with Bandit..."
if command_exists bandit; then
if bandit -r . -f json -o bandit-report.json; then
echo " ✅ Bandit security linting check passed"
else
echo " ❌ Bandit found security issues"
echo " 💡 Review bandit-report.json for details"
exit 1
fi
else
echo " ⚠️ Bandit not installed, skipping security linting"
echo " 💡 Install with: pip install bandit"
fi

echo "✅ All code quality checks passed!"
85 changes: 85 additions & 0 deletions .husky/hooks/snyk-scan.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
#!/usr/bin/env python3
"""
Snyk security scanning hook for Husky-style pre-commit setup.
This hook runs Snyk security scanning on Python dependencies.
"""

import os
import sys
import subprocess
import json
from pathlib import Path


def run_snyk_scan():
"""Run Snyk security scan on Python dependencies."""

# Check if Snyk CLI is available
try:
subprocess.run(['snyk', '--version'], capture_output=True, check=True)
except (subprocess.CalledProcessError, FileNotFoundError):
print("❌ Snyk CLI not found. Please install it first:")
print(" npm install -g snyk")
print(" or visit: https://snyk.io/docs/using-snyk/")
return 1

# Check if SNYK_TOKEN is set
if not os.getenv('SNYK_TOKEN'):
print("⚠️ SNYK_TOKEN environment variable not set.")
print(" Please set it with: export SNYK_TOKEN=your_token")
print(" You can get a token from: https://app.snyk.io/account")
return 0 # Don't fail the commit, just warn

# Check for requirements.txt
requirements_files = ['requirements.txt', 'setup.py']
found_requirements = False

for req_file in requirements_files:
if Path(req_file).exists():
found_requirements = True
break

if not found_requirements:
print("⚠️ No requirements.txt or setup.py found. Skipping Snyk scan.")
return 0

print("🔍 Running Snyk security scan...")

try:
# Run Snyk test on Python dependencies
result = subprocess.run([
'snyk', 'test',
'--severity-threshold=high',
'--json'
], capture_output=True, text=True, check=False)

if result.returncode == 0:
print("✅ Snyk scan completed - no high severity vulnerabilities found")
return 0
else:
# Parse JSON output to show vulnerabilities
try:
vulns = json.loads(result.stdout)
if 'vulnerabilities' in vulns:
print("❌ High severity vulnerabilities found:")
for vuln in vulns['vulnerabilities']:
if vuln.get('severity') == 'high':
print(f" - {vuln.get('title', 'Unknown')} in {vuln.get('packageName', 'Unknown')}")
print(f" CVSS Score: {vuln.get('cvssScore', 'N/A')}")
print(f" More info: {vuln.get('url', 'N/A')}")
print()

print("💡 To fix vulnerabilities, run: snyk wizard")
return 1
except json.JSONDecodeError:
print("❌ Snyk scan failed with errors:")
print(result.stderr)
return 1

except Exception as e:
print(f"❌ Error running Snyk scan: {e}")
return 1


if __name__ == '__main__':
sys.exit(run_snyk_scan())
35 changes: 35 additions & 0 deletions .husky/hooks/talisman-check.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#!/usr/bin/env bash
"""
Talisman secrets detection hook for Husky-style pre-commit setup.
This hook runs Talisman to detect potential secrets in commits.
"""

# Check if Talisman is available
if ! command -v talisman &> /dev/null; then
echo "❌ Talisman not found. Please install it first:"
echo " # macOS"
echo " brew install talisman"
echo " # Linux"
echo " curl -sL https://github.com/thoughtworks/talisman/releases/latest/download/talisman_linux_amd64 -o talisman"
echo " chmod +x talisman"
echo " sudo mv talisman /usr/local/bin/"
exit 1
fi

echo "🔐 Running Talisman secrets detection..."

# Run Talisman with pre-commit hook
if talisman --githook pre-commit; then
echo "✅ Talisman check passed - no secrets detected"
exit 0
else
echo "❌ Talisman found potential secrets in your changes"
echo ""
echo "💡 To fix this:"
echo "1. Review the files mentioned above"
echo "2. Remove any actual secrets from your code"
echo "3. If the file contains legitimate test data, add it to .talismanrc:"
echo " talisman --checksum path/to/file"
echo " # Then add the checksum to .talismanrc"
exit 1
fi
44 changes: 44 additions & 0 deletions .husky/hooks/test-runner.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#!/usr/bin/env bash
"""
Test runner hook for Husky-style pre-push setup.
This hook runs tests and coverage checks before pushing.
"""

echo "🧪 Running tests and coverage checks..."

# Function to check if a command exists
command_exists() {
command -v "$1" >/dev/null 2>&1
}

# Check if pytest is available
if ! command_exists pytest; then
echo "❌ pytest not found. Please install it first:"
echo " pip install pytest pytest-cov"
exit 1
fi

# Run tests
echo " - Running tests..."
if pytest --html=tests/test-report/test-report.html; then
echo " ✅ All tests passed"
else
echo " ❌ Tests failed. Please fix before pushing."
exit 1
fi

# Run coverage check
echo " - Checking test coverage..."
if command_exists pytest; then
if pytest --cov=contentstack_utils --cov-report=term-missing; then
echo " ✅ Coverage check completed"
else
echo " ❌ Coverage check failed. Please improve test coverage."
exit 1
fi
else
echo " ⚠️ pytest-cov not installed, skipping coverage check"
echo " 💡 Install with: pip install pytest-cov"
fi

echo "✅ All test checks passed!"
69 changes: 69 additions & 0 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
#!/usr/bin/env sh
# Pre-commit hook to run Talisman and Snyk scans, completing both before deciding to commit

# Function to check if a command exists
command_exists() {
command -v "$1" >/dev/null 2>&1
}

# Check if Talisman is installed
if ! command_exists talisman; then
echo "Error: Talisman is not installed. Please install it and try again."
exit 1
fi

# Check if Snyk is installed
if ! command_exists snyk; then
echo "Error: Snyk is not installed. Please install it and try again."
exit 1
fi

# Allow bypassing the hook with an environment variable
if [ "$SKIP_HOOK" = "1" ]; then
echo "Skipping Talisman and Snyk scans (SKIP_HOOK=1)."
exit 0
fi

# Initialize variables to track scan results
talisman_failed=false
snyk_failed=false

# Run Talisman secret scan
echo "Running Talisman secret scan..."
talisman --githook pre-commit > talisman_output.log 2>&1
talisman_exit_code=$?

if [ $talisman_exit_code -eq 0 ]; then
echo "Talisman scan passed: No secrets found."
else
echo "Talisman scan failed (exit code $talisman_exit_code). See talisman_output.log for details."
talisman_failed=true
fi

# Run Snyk vulnerability scan (continues even if Talisman failed)
echo "Running Snyk vulnerability scan..."
snyk test --all-projects --fail-on=all > snyk_output.log 2>&1
snyk_exit_code=$?

if [ $snyk_exit_code -eq 0 ]; then
echo "Snyk scan passed: No vulnerabilities found."
elif [ $snyk_exit_code -eq 1 ]; then
echo "Snyk found vulnerabilities. See snyk_output.log for details."
snyk_failed=true
else
echo "Snyk scan failed with error (exit code $snyk_exit_code). See snyk_output.log for details."
snyk_failed=true
fi

# Evaluate results after both scans
if [ "$talisman_failed" = true ] || [ "$snyk_failed" = true ]; then
echo "Commit aborted due to issues found in one or both scans."
[ "$talisman_failed" = true ] && echo "- Talisman issues: Check talisman_output.log"
[ "$snyk_failed" = true ] && echo "- Snyk issues: Check snyk_output.log"
exit 1
fi

# If both scans pass, allow the commit
echo "All scans passed. Proceeding with commit."
rm -f talisman_output.log snyk_output.log
exit 0
34 changes: 34 additions & 0 deletions .husky/pre-push
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#!/usr/bin/env sh

echo "🚀 Running pre-push checks..."

# Activate virtual environment if it exists
if [ -d "venv" ]; then
echo "🔧 Activating virtual environment..."
source venv/bin/activate
fi

# Run tests to ensure code quality
echo "🧪 Running tests..."
if ! pytest --html=tests/test-report/test-report.html; then
echo "❌ Tests failed. Please fix before pushing."
exit 1
fi

# Run coverage check
echo "📊 Checking test coverage..."
if ! pytest --cov=contentstack_utils --cov-report=term-missing; then
echo "❌ Coverage check failed. Please improve test coverage."
exit 1
fi

# Run security scan on dependencies (optional)
if [ -n "$SNYK_TOKEN" ]; then
echo "🔍 Running comprehensive Snyk scan..."
if ! snyk test --severity-threshold=high; then
echo "❌ High severity vulnerabilities found. Please fix before pushing."
exit 1
fi
fi

echo "✅ All pre-push checks passed!"
10 changes: 8 additions & 2 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
pip~=25.2
pip~=26.0
setuptools==80.3.1
lxml~=5.4.0
ruff==0.11.5
ruff==0.11.5
pytest>=7.0.0
pytest-cov>=4.0.0
pytest-html>=4.2.0
black>=23.0.0
flake8>=6.0.0
isort>=5.0.0
Loading