diff --git a/.husky/hooks/code-quality.sh b/.husky/hooks/code-quality.sh new file mode 100755 index 0000000..b556a90 --- /dev/null +++ b/.husky/hooks/code-quality.sh @@ -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!" diff --git a/.husky/hooks/snyk-scan.py b/.husky/hooks/snyk-scan.py new file mode 100755 index 0000000..c7b4dc0 --- /dev/null +++ b/.husky/hooks/snyk-scan.py @@ -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()) diff --git a/.husky/hooks/talisman-check.sh b/.husky/hooks/talisman-check.sh new file mode 100755 index 0000000..9e0fd06 --- /dev/null +++ b/.husky/hooks/talisman-check.sh @@ -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 diff --git a/.husky/hooks/test-runner.sh b/.husky/hooks/test-runner.sh new file mode 100755 index 0000000..aa5de03 --- /dev/null +++ b/.husky/hooks/test-runner.sh @@ -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/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 --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!" diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100755 index 0000000..da3763f --- /dev/null +++ b/.husky/pre-commit @@ -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 diff --git a/.husky/pre-push b/.husky/pre-push new file mode 100755 index 0000000..a5b1923 --- /dev/null +++ b/.husky/pre-push @@ -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/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 --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!" diff --git a/requirements.txt b/requirements.txt index e47143c..60ac155 100644 --- a/requirements.txt +++ b/requirements.txt @@ -60,4 +60,8 @@ cachetools~=5.4.0 tomlkit~=0.13.2 urllib3==2.5.0 exceptiongroup~=1.2.2 -iniconfig~=2.0.0 \ No newline at end of file +iniconfig~=2.0.0 +pytest-cov>=4.0.0 +pytest-html>=3.0.0 +black>=23.0.0 +flake8>=6.0.0 \ No newline at end of file