Skip to content

Added local run options #21

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Jun 19, 2025
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,4 @@ test.py
file_generator.py
.env
*.md
test_results
49 changes: 48 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,58 @@ jobs:
security/detect-non-literal-regexp,
security/detect-object-injection
# Log forwarding
# Log output
sumo_logic_enabled: true
sumo_logic_http_source_url: https://example/url
ms_sentinel_enabled: true
ms_sentinel_workspace_id: REPLACE_ME
ms_sentinel_shared_key: REPLACE_ME

# Scan scope settings
scan_all: false # Set to true to always scan the whole directory
scan_files: "" # Comma-separated list of files to scan (overrides git diff)
```
## Local Development & Testing
You can run the security-wrapper locally using Docker. This is useful for testing changes or scanning code outside of GitHub Actions.
### Build the Docker Image
```sh
git clone [email protected]:SocketDev/security-wrapper.git

# Build the Docker image
docker build -t socketdev/security-wrapper .
```

### Run the Security Wrapper Locally

```sh
docker run --rm --name security-wrapper \
-v "$PWD:/code" \
-e "GIT_REPO=socketdev-demo/sast-testing" \
-e "GITHUB_REPOSITORY=socketdev-demo/sast-testing" \
-e "GITHUB_WORKSPACE=/code" \
-e "INPUT_CONSOLE_ENABLED=true" \
# Uncomment and set if you want to scan images (requires Docker-in-Docker)
# -e "INPUT_DOCKER_IMAGES=trickyhu/sigsci-rule-editor:latest,socketdev/cli:latest" \
-e "INPUT_DOCKERFILE_ENABLED=true" \
-e "INPUT_DOCKERFILES=Dockerfile,Dockerfile.sigsci" \
-e "INPUT_ESLINT_SAST_ENABLED=true" \
-e "INPUT_FINDING_SEVERITIES=critical" \
-e "INPUT_GOSEC_SAST_ENABLED=true" \
-e "INPUT_IMAGE_ENABLED=true" \
-e "INPUT_PYTHON_SAST_ENABLED=true" \
-e "PYTHONUNBUFFERED=1" \
-e "INPUT_SECRET_SCANNING_ENABLED=true" \
-e "SOCKET_SCM_DISABLED=true" \
-e "INPUT_SOCKET_CONSOLE_MODE=json" \
socketdev/security-wrapper
```

**Notes:**
- You can adjust the environment variables to enable/disable specific scanners.
- For image scanning, Docker-in-Docker must be enabled, and you may need to add a `docker pull` step before running.
- Results will be printed to the console or output as JSON, depending on `INPUT_SOCKET_CONSOLE_MODE`.
- You can also run the wrapper directly with Bash and Python for rapid local development (see `entrypoint.sh`).
10 changes: 10 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,16 @@ inputs:
required: false
default: "REPLACE_ME"

# Scan Scope Configuration
scan_all:
description: "If true, always scan the whole directory regardless of git or file list."
required: false
default: "false"
scan_files:
description: "Comma-separated list of files to scan. If not set, will use git diff or scan all."
required: false
default: ""

branding:
icon: "shield"
color: "blue"
223 changes: 133 additions & 90 deletions entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,115 @@ GOSEC_RULES=${INPUT_GOSEC_RULES:-}
TRIVY_EXCLUDE_DIR=${INPUT_TRIVY_EXCLUDE_DIR:-}
TRIVY_RULES=${INPUT_TRIVY_RULES:-}

# Set output directory for temp files
if [[ -n "$OUTPUT_DIR" ]]; then
TEMP_OUTPUT_DIR="$OUTPUT_DIR"
else
TEMP_OUTPUT_DIR="$(pwd)"
fi

# Run Trivy on Container Images if enabled
if [[ "$INPUT_TRIVY_IMAGE_ENABLED" == "true" ]]; then
echo "Running Trivy on Container Images"
IFS=',' read -ra DOCKER_IMAGES <<< "${INPUT_DOCKER_IMAGES}"
for image in "${DOCKER_IMAGES[@]}"; do
echo "Scanning image: $image"
trivy image --scanners vuln --format json --output "$TEMP_OUTPUT_DIR/trivy_image_${image//\//_}.json" "$image" || :
done
fi

# Run Trivy on Dockerfiles if enabled
if [[ "$INPUT_TRIVY_DOCKERFILE_ENABLED" == "true" ]]; then
IFS=',' read -ra DOCKERFILES <<< "${INPUT_DOCKERFILES}"
for dockerfile in "${DOCKERFILES[@]}"; do
echo "Scanning Dockerfile: $dockerfile"
trivy config --format json --output "$TEMP_OUTPUT_DIR/trivy_dockerfile_${dockerfile//\//_}.json" "$GITHUB_WORKSPACE/$dockerfile" || :
done
fi

# Run Secret Scanning (Trufflehog) if enabled
if [[ "$INPUT_SECRET_SCANNING_ENABLED" == "true" ]]; then
echo "Running Secret Scanning with Trufflehog"
trufflehog_cmd="trufflehog filesystem "
TRUFFLEHOG_EXCLUDE_FILE=$(mktemp)
if [[ -n "$TRUFFLEHOG_EXCLUDE_DIR" ]]; then
IFS=',' read -ra EXCLUDE_DIRS <<< "$TRUFFLEHOG_EXCLUDE_DIR"
for dir in "${EXCLUDE_DIRS[@]}"; do
echo "$dir" >> "$TRUFFLEHOG_EXCLUDE_FILE"
done
trufflehog_cmd+=" -x $TRUFFLEHOG_EXCLUDE_FILE"
fi
if [[ -n "$TRUFFLEHOG_RULES" ]]; then
trufflehog_cmd+=" --rules $TRUFFLEHOG_RULES"
fi
trufflehog_cmd+=" --no-verification -j $GITHUB_WORKSPACE > $TEMP_OUTPUT_DIR/trufflehog_output.json"
eval $trufflehog_cmd || :
fi

# Run ESLint (JavaScript SAST) if enabled
if [[ "${INPUT_JAVASCRIPT_SAST_ENABLED:-false}" == "true" ]]; then
echo "Running ESLint"
ESLINT_EXCLUDE_DIR=${INPUT_ESLINT_EXCLUDE_DIR:-}
ESLINT_RULES=${INPUT_ESLINT_RULES:-}
# POSIX-compatible file collection (replace mapfile)
scan_files=()
if [[ "$INPUT_SCAN_ALL" == "true" ]]; then
while IFS= read -r file; do
scan_files+=("$file")
done < <(find . -type f \( -name '*.py' -o -name '*.go' -o -name '*.js' -o -name '*.jsx' -o -name '*.ts' -o -name '*.tsx' \))
elif [[ -n "$INPUT_SCAN_FILES" ]]; then
IFS=',' read -ra scan_files <<< "$INPUT_SCAN_FILES"
else
if [[ -d .git ]]; then
while IFS= read -r file; do
scan_files+=("$file")
done < <(git diff --name-only HEAD~1 HEAD)
else
while IFS= read -r file; do
scan_files+=("$file")
done < <(find . -type f \( -name '*.py' -o -name '*.go' -o -name '*.js' -o -name '*.jsx' -o -name '*.ts' -o -name '*.tsx' \))
fi
fi

# Separate files by language
python_files=()
go_files=()
js_files=()
for file in "${scan_files[@]}"; do
case "$file" in
*.py) python_files+=("$file") ;;
*.go) go_files+=("$file") ;;
*.js|*.jsx|*.ts|*.tsx) js_files+=("$file") ;;
esac
done

# Run Bandit on Python files
if [[ "${#python_files[@]}" -gt 0 && "$INPUT_PYTHON_SAST_ENABLED" == "true" ]]; then
echo "Running Bandit on Python files: ${python_files[*]}"
bandit_cmd="bandit -f json -o $TEMP_OUTPUT_DIR/bandit_output.json ${python_files[*]}"
if [[ -n "$BANDIT_EXCLUDE_DIR" ]]; then
bandit_cmd+=" --exclude $BANDIT_EXCLUDE_DIR"
fi
if [[ -n "$BANDIT_RULES" ]]; then
bandit_cmd+=" --skip $BANDIT_RULES"
fi
echo $bandit_cmd
eval $bandit_cmd || :
fi

if [[ -z "$ESLINT_RULES" ]]; then
echo "Using default ESLint rules"
ESLINT_RULES=$(cat <<'EOF'
# Run Gosec on Go files
if [[ "${#go_files[@]}" -gt 0 && "$INPUT_GOLANG_SAST_ENABLED" == "true" ]]; then
echo "Running Gosec on Go files: ${go_files[*]}"
gosec_cmd="gosec -fmt json -out $TEMP_OUTPUT_DIR/gosec_output.json ${go_files[*]}"
if [[ -n "$GOSEC_EXCLUDE_DIR" ]]; then
gosec_cmd+=" -exclude-dir=$GOSEC_EXCLUDE_DIR"
fi
if [[ -n "$GOSEC_RULES" ]]; then
gosec_cmd+=" -severity=$GOSEC_RULES"
fi
eval $gosec_cmd || :
fi

# ESLint rules setup (needed for JS/TS SAST)
ESLINT_EXCLUDE_DIR=${INPUT_ESLINT_EXCLUDE_DIR:-}
ESLINT_RULES=${INPUT_ESLINT_RULES:-}
if [[ -z "$ESLINT_RULES" ]]; then
ESLINT_RULES=$(cat <<'EOF'
security/detect-eval-with-expression,
security/detect-non-literal-require,
security/detect-non-literal-fs-filename,
Expand Down Expand Up @@ -75,14 +174,14 @@ security/detect-object-injection,
@typescript-eslint/prefer-as-const
EOF
)
fi
fi

# Convert rule list to JSON map: "rule-name": "error"
ESLINT_RULES_JSON=$(echo "$ESLINT_RULES" | tr ',' '\n' | sed '/^\s*$/d' | awk '{printf "\"%s\": \"error\",\n", $0}' | sed '$s/,$//')
# Convert rule list to JSON map: "rule-name": "error"
ESLINT_RULES_JSON=$(echo "$ESLINT_RULES" | tr ',' '\n' | sed '/^\s*$/d' | awk '{printf "\"%s\": \"error\",\n", $0}' | sed '$s/,$//')

if [[ ! -f "$WORKSPACE/eslint.config.mjs" ]]; then
echo "Adding fallback ESLint config"
cat <<EOF > "$WORKSPACE/eslint.config.mjs"
if [[ ! -f "$WORKSPACE/eslint.config.mjs" ]]; then
echo "Adding fallback ESLint config"
cat <<EOF > "$WORKSPACE/eslint.config.mjs"
export default [
{
files: ['**/*.js', '**/*.jsx', '**/*.ts', '**/*.tsx'],
Expand All @@ -92,94 +191,38 @@ $ESLINT_RULES_JSON
},
];
EOF
fi

eslint_cmd="npx --yes eslint --config $WORKSPACE/eslint.config.mjs $WORKSPACE --ext .js,.jsx,.ts,.tsx --format json --output-file $OUTPUT_DIR/eslint_output.json"
fi

# Run ESLint on JS/TS files
if [[ "${#js_files[@]}" -gt 0 && "${INPUT_JAVASCRIPT_SAST_ENABLED:-false}" == "true" ]]; then
echo "Running ESLint on JS/TS files: ${js_files[*]}"
eslint_cmd="npx --yes eslint --config $WORKSPACE/eslint.config.mjs ${js_files[*]} --ext .js,.jsx,.ts,.tsx --format json --output-file $TEMP_OUTPUT_DIR/eslint_output.json"
if [[ -n "$ESLINT_EXCLUDE_DIR" ]]; then
IFS=',' read -ra EXCLUDES <<< "$ESLINT_EXCLUDE_DIR"
for exclude in "${EXCLUDES[@]}"; do
eslint_cmd+=" --ignore-pattern $exclude"
done
fi

eval $eslint_cmd || :
fi


# Run Bandit (Python SAST) if enabled
if [[ "$INPUT_PYTHON_SAST_ENABLED" == "true" ]]; then
echo "Running Bandit"
bandit_cmd="bandit -r $GITHUB_WORKSPACE -f json -o /tmp/bandit_output.json"
if [[ -n "$BANDIT_EXCLUDE_DIR" ]]; then
bandit_cmd+=" --exclude $BANDIT_EXCLUDE_DIR"
fi
if [[ -n "$BANDIT_RULES" ]]; then
bandit_cmd+=" --skip $BANDIT_RULES"
fi
echo $bandit_cmd
eval $bandit_cmd || :
fi

# Run Gosec (Golang SAST) if enabled
if [[ "$INPUT_GOLANG_SAST_ENABLED" == "true" ]]; then
echo "Running Gosec"
gosec_cmd="gosec -fmt json -out /tmp/gosec_output.json "
if [[ -n "$GOSEC_EXCLUDE_DIR" ]]; then
gosec_cmd+=" -exclude-dir=$GOSEC_EXCLUDE_DIR"
fi
if [[ -n "$GOSEC_RULES" ]]; then
gosec_cmd+=" -severity=$GOSEC_RULES"
fi
gosec_cmd+=" $GITHUB_WORKSPACE/..."
eval $gosec_cmd || :
fi

# Run Trivy on Container Images if enabled
if [[ "$INPUT_TRIVY_IMAGE_ENABLED" == "true" ]]; then
echo "Running Trivy on Container Images"
IFS=',' read -ra DOCKER_IMAGES <<< "${INPUT_DOCKER_IMAGES}"
for image in "${DOCKER_IMAGES[@]}"; do
echo "Scanning image: $image"
trivy image --scanners vuln --format json --output "/tmp/trivy_image_${image//\//_}.json" "$image" || :
done
fi

# Run Trivy on Dockerfiles if enabled
if [[ "$INPUT_TRIVY_DOCKERFILE_ENABLED" == "true" ]]; then
IFS=',' read -ra DOCKERFILES <<< "${INPUT_DOCKERFILES}"
for dockerfile in "${DOCKERFILES[@]}"; do
echo "Scanning Dockerfile: $dockerfile"
trivy config --format json --output "/tmp/trivy_dockerfile_${dockerfile//\//_}.json" "$GITHUB_WORKSPACE/$dockerfile" || :
done
# Move output files (no-op if already in correct place)
# Only cd in GitHub Actions, not local
if [ "$LOCAL_TESTING" != "true" ]; then
cd "$WORKSPACE"
fi

# Run Secret Scanning (Trufflehog) if enabled
if [[ "$INPUT_SECRET_SCANNING_ENABLED" == "true" ]]; then
echo "Running Secret Scanning with Trufflehog"
trufflehog_cmd="trufflehog filesystem "
TRUFFLEHOG_EXCLUDE_FILE=$(mktemp)
if [[ -n "$TRUFFLEHOG_EXCLUDE_DIR" ]]; then
IFS=',' read -ra EXCLUDE_DIRS <<< "$TRUFFLEHOG_EXCLUDE_DIR"
for dir in "${EXCLUDE_DIRS[@]}"; do
echo "$dir" >> "$TRUFFLEHOG_EXCLUDE_FILE"
done
trufflehog_cmd+=" -x $TRUFFLEHOG_EXCLUDE_FILE"
fi
if [[ -n "$TRUFFLEHOG_RULES" ]]; then
trufflehog_cmd+=" --rules $TRUFFLEHOG_RULES"
fi
trufflehog_cmd+=" --no-verification -j $GITHUB_WORKSPACE > /tmp/trufflehog_output.json"
eval $trufflehog_cmd || :
# Run the Python script from the correct directory and path
if [[ -n "$PY_SCRIPT_PATH" ]]; then
FINAL_PY_SCRIPT_PATH="$PY_SCRIPT_PATH"
elif [[ "$DEV_MODE" == "true" ]]; then
FINAL_PY_SCRIPT_PATH="$WORKSPACE/src/socket_external_tools_runner.py"
else
FINAL_PY_SCRIPT_PATH="$WORKSPACE/socket_external_tools_runner.py"
fi

# Execute the custom Python script to process findings
if [ "$LOCAL_TESTING" != "true" ]; then
cd /
fi
mv /tmp/*.json .
if [ "$LOCAL_TESTING" != "true" ]; then
python socket_external_tools_runner.py
if [[ -f "$FINAL_PY_SCRIPT_PATH" ]]; then
python "$FINAL_PY_SCRIPT_PATH"
else
python socket_external_tools_runner.py
echo "Error: Python script not found at $FINAL_PY_SCRIPT_PATH" >&2
exit 1
fi
1 change: 1 addition & 0 deletions node_modules/.bin/cli

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading