Skip to content

Commit 24e78eb

Browse files
authored
Added local run options (#21)
* Added local run options * Adding support for only changed files * Ignore typing issue * Added test file for python for triggering rules * Improved the logic for running connectors based on input settings and what is outputted * Updated documentation * Updated readme
1 parent 863909c commit 24e78eb

16 files changed

Lines changed: 212749 additions & 154 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,4 @@ test.py
2222
file_generator.py
2323
.env
2424
*.md
25+
test_results

README.md

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,11 +61,58 @@ jobs:
6161
security/detect-non-literal-regexp,
6262
security/detect-object-injection
6363
64-
# Log forwarding
64+
# Log output
6565
sumo_logic_enabled: true
6666
sumo_logic_http_source_url: https://example/url
6767
ms_sentinel_enabled: true
6868
ms_sentinel_workspace_id: REPLACE_ME
6969
ms_sentinel_shared_key: REPLACE_ME
7070

71+
# Scan scope settings
72+
scan_all: false # Set to true to always scan the whole directory
73+
scan_files: "" # Comma-separated list of files to scan (overrides git diff)
7174
```
75+
76+
## Local Development & Testing
77+
78+
You can run the security-wrapper locally using Docker. This is useful for testing changes or scanning code outside of GitHub Actions.
79+
80+
### Build the Docker Image
81+
82+
```sh
83+
git clone git@github.com:SocketDev/security-wrapper.git
84+
85+
# Build the Docker image
86+
docker build -t socketdev/security-wrapper .
87+
```
88+
89+
### Run the Security Wrapper Locally
90+
91+
```sh
92+
docker run --rm --name security-wrapper \
93+
-v "$PWD:/code" \
94+
-e "GIT_REPO=socketdev-demo/sast-testing" \
95+
-e "GITHUB_REPOSITORY=socketdev-demo/sast-testing" \
96+
-e "GITHUB_WORKSPACE=/code" \
97+
-e "INPUT_CONSOLE_ENABLED=true" \
98+
# Uncomment and set if you want to scan images (requires Docker-in-Docker)
99+
# -e "INPUT_DOCKER_IMAGES=trickyhu/sigsci-rule-editor:latest,socketdev/cli:latest" \
100+
-e "INPUT_DOCKERFILE_ENABLED=true" \
101+
-e "INPUT_DOCKERFILES=Dockerfile,Dockerfile.sigsci" \
102+
-e "INPUT_ESLINT_SAST_ENABLED=true" \
103+
-e "INPUT_FINDING_SEVERITIES=critical" \
104+
-e "INPUT_GOSEC_SAST_ENABLED=true" \
105+
-e "INPUT_IMAGE_ENABLED=true" \
106+
-e "INPUT_PYTHON_SAST_ENABLED=true" \
107+
-e "PYTHONUNBUFFERED=1" \
108+
-e "INPUT_SECRET_SCANNING_ENABLED=true" \
109+
-e "SOCKET_SCM_DISABLED=true" \
110+
-e "INPUT_SOCKET_CONSOLE_MODE=json" \
111+
socketdev/security-wrapper
112+
```
113+
114+
**Notes:**
115+
- You can adjust the environment variables to enable/disable specific scanners.
116+
- For image scanning, Docker-in-Docker must be enabled, and you may need to add a `docker pull` step before running.
117+
- Results will be printed to the console or output as JSON, depending on `INPUT_SOCKET_CONSOLE_MODE`.
118+
- You can also run the wrapper directly with Bash and Python for rapid local development (see `entrypoint.sh`).

action.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,16 @@ inputs:
129129
required: false
130130
default: "REPLACE_ME"
131131

132+
# Scan Scope Configuration
133+
scan_all:
134+
description: "If true, always scan the whole directory regardless of git or file list."
135+
required: false
136+
default: "false"
137+
scan_files:
138+
description: "Comma-separated list of files to scan. If not set, will use git diff or scan all."
139+
required: false
140+
default: ""
141+
132142
branding:
133143
icon: "shield"
134144
color: "blue"

entrypoint.sh

Lines changed: 133 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,115 @@ GOSEC_RULES=${INPUT_GOSEC_RULES:-}
1313
TRIVY_EXCLUDE_DIR=${INPUT_TRIVY_EXCLUDE_DIR:-}
1414
TRIVY_RULES=${INPUT_TRIVY_RULES:-}
1515

16+
# Set output directory for temp files
17+
if [[ -n "$OUTPUT_DIR" ]]; then
18+
TEMP_OUTPUT_DIR="$OUTPUT_DIR"
19+
else
20+
TEMP_OUTPUT_DIR="$(pwd)"
21+
fi
22+
23+
# Run Trivy on Container Images if enabled
24+
if [[ "$INPUT_TRIVY_IMAGE_ENABLED" == "true" ]]; then
25+
echo "Running Trivy on Container Images"
26+
IFS=',' read -ra DOCKER_IMAGES <<< "${INPUT_DOCKER_IMAGES}"
27+
for image in "${DOCKER_IMAGES[@]}"; do
28+
echo "Scanning image: $image"
29+
trivy image --scanners vuln --format json --output "$TEMP_OUTPUT_DIR/trivy_image_${image//\//_}.json" "$image" || :
30+
done
31+
fi
32+
33+
# Run Trivy on Dockerfiles if enabled
34+
if [[ "$INPUT_TRIVY_DOCKERFILE_ENABLED" == "true" ]]; then
35+
IFS=',' read -ra DOCKERFILES <<< "${INPUT_DOCKERFILES}"
36+
for dockerfile in "${DOCKERFILES[@]}"; do
37+
echo "Scanning Dockerfile: $dockerfile"
38+
trivy config --format json --output "$TEMP_OUTPUT_DIR/trivy_dockerfile_${dockerfile//\//_}.json" "$GITHUB_WORKSPACE/$dockerfile" || :
39+
done
40+
fi
41+
42+
# Run Secret Scanning (Trufflehog) if enabled
43+
if [[ "$INPUT_SECRET_SCANNING_ENABLED" == "true" ]]; then
44+
echo "Running Secret Scanning with Trufflehog"
45+
trufflehog_cmd="trufflehog filesystem "
46+
TRUFFLEHOG_EXCLUDE_FILE=$(mktemp)
47+
if [[ -n "$TRUFFLEHOG_EXCLUDE_DIR" ]]; then
48+
IFS=',' read -ra EXCLUDE_DIRS <<< "$TRUFFLEHOG_EXCLUDE_DIR"
49+
for dir in "${EXCLUDE_DIRS[@]}"; do
50+
echo "$dir" >> "$TRUFFLEHOG_EXCLUDE_FILE"
51+
done
52+
trufflehog_cmd+=" -x $TRUFFLEHOG_EXCLUDE_FILE"
53+
fi
54+
if [[ -n "$TRUFFLEHOG_RULES" ]]; then
55+
trufflehog_cmd+=" --rules $TRUFFLEHOG_RULES"
56+
fi
57+
trufflehog_cmd+=" --no-verification -j $GITHUB_WORKSPACE > $TEMP_OUTPUT_DIR/trufflehog_output.json"
58+
eval $trufflehog_cmd || :
59+
fi
1660

17-
# Run ESLint (JavaScript SAST) if enabled
18-
if [[ "${INPUT_JAVASCRIPT_SAST_ENABLED:-false}" == "true" ]]; then
19-
echo "Running ESLint"
20-
ESLINT_EXCLUDE_DIR=${INPUT_ESLINT_EXCLUDE_DIR:-}
21-
ESLINT_RULES=${INPUT_ESLINT_RULES:-}
61+
# POSIX-compatible file collection (replace mapfile)
62+
scan_files=()
63+
if [[ "$INPUT_SCAN_ALL" == "true" ]]; then
64+
while IFS= read -r file; do
65+
scan_files+=("$file")
66+
done < <(find . -type f \( -name '*.py' -o -name '*.go' -o -name '*.js' -o -name '*.jsx' -o -name '*.ts' -o -name '*.tsx' \))
67+
elif [[ -n "$INPUT_SCAN_FILES" ]]; then
68+
IFS=',' read -ra scan_files <<< "$INPUT_SCAN_FILES"
69+
else
70+
if [[ -d .git ]]; then
71+
while IFS= read -r file; do
72+
scan_files+=("$file")
73+
done < <(git diff --name-only HEAD~1 HEAD)
74+
else
75+
while IFS= read -r file; do
76+
scan_files+=("$file")
77+
done < <(find . -type f \( -name '*.py' -o -name '*.go' -o -name '*.js' -o -name '*.jsx' -o -name '*.ts' -o -name '*.tsx' \))
78+
fi
79+
fi
80+
81+
# Separate files by language
82+
python_files=()
83+
go_files=()
84+
js_files=()
85+
for file in "${scan_files[@]}"; do
86+
case "$file" in
87+
*.py) python_files+=("$file") ;;
88+
*.go) go_files+=("$file") ;;
89+
*.js|*.jsx|*.ts|*.tsx) js_files+=("$file") ;;
90+
esac
91+
done
92+
93+
# Run Bandit on Python files
94+
if [[ "${#python_files[@]}" -gt 0 && "$INPUT_PYTHON_SAST_ENABLED" == "true" ]]; then
95+
echo "Running Bandit on Python files: ${python_files[*]}"
96+
bandit_cmd="bandit -f json -o $TEMP_OUTPUT_DIR/bandit_output.json ${python_files[*]}"
97+
if [[ -n "$BANDIT_EXCLUDE_DIR" ]]; then
98+
bandit_cmd+=" --exclude $BANDIT_EXCLUDE_DIR"
99+
fi
100+
if [[ -n "$BANDIT_RULES" ]]; then
101+
bandit_cmd+=" --skip $BANDIT_RULES"
102+
fi
103+
echo $bandit_cmd
104+
eval $bandit_cmd || :
105+
fi
22106

23-
if [[ -z "$ESLINT_RULES" ]]; then
24-
echo "Using default ESLint rules"
25-
ESLINT_RULES=$(cat <<'EOF'
107+
# Run Gosec on Go files
108+
if [[ "${#go_files[@]}" -gt 0 && "$INPUT_GOLANG_SAST_ENABLED" == "true" ]]; then
109+
echo "Running Gosec on Go files: ${go_files[*]}"
110+
gosec_cmd="gosec -fmt json -out $TEMP_OUTPUT_DIR/gosec_output.json ${go_files[*]}"
111+
if [[ -n "$GOSEC_EXCLUDE_DIR" ]]; then
112+
gosec_cmd+=" -exclude-dir=$GOSEC_EXCLUDE_DIR"
113+
fi
114+
if [[ -n "$GOSEC_RULES" ]]; then
115+
gosec_cmd+=" -severity=$GOSEC_RULES"
116+
fi
117+
eval $gosec_cmd || :
118+
fi
119+
120+
# ESLint rules setup (needed for JS/TS SAST)
121+
ESLINT_EXCLUDE_DIR=${INPUT_ESLINT_EXCLUDE_DIR:-}
122+
ESLINT_RULES=${INPUT_ESLINT_RULES:-}
123+
if [[ -z "$ESLINT_RULES" ]]; then
124+
ESLINT_RULES=$(cat <<'EOF'
26125
security/detect-eval-with-expression,
27126
security/detect-non-literal-require,
28127
security/detect-non-literal-fs-filename,
@@ -75,14 +174,14 @@ security/detect-object-injection,
75174
@typescript-eslint/prefer-as-const
76175
EOF
77176
)
78-
fi
177+
fi
79178

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

83-
if [[ ! -f "$WORKSPACE/eslint.config.mjs" ]]; then
84-
echo "Adding fallback ESLint config"
85-
cat <<EOF > "$WORKSPACE/eslint.config.mjs"
182+
if [[ ! -f "$WORKSPACE/eslint.config.mjs" ]]; then
183+
echo "Adding fallback ESLint config"
184+
cat <<EOF > "$WORKSPACE/eslint.config.mjs"
86185
export default [
87186
{
88187
files: ['**/*.js', '**/*.jsx', '**/*.ts', '**/*.tsx'],
@@ -92,94 +191,38 @@ $ESLINT_RULES_JSON
92191
},
93192
];
94193
EOF
95-
fi
96-
97-
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"
194+
fi
98195

196+
# Run ESLint on JS/TS files
197+
if [[ "${#js_files[@]}" -gt 0 && "${INPUT_JAVASCRIPT_SAST_ENABLED:-false}" == "true" ]]; then
198+
echo "Running ESLint on JS/TS files: ${js_files[*]}"
199+
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"
99200
if [[ -n "$ESLINT_EXCLUDE_DIR" ]]; then
100201
IFS=',' read -ra EXCLUDES <<< "$ESLINT_EXCLUDE_DIR"
101202
for exclude in "${EXCLUDES[@]}"; do
102203
eslint_cmd+=" --ignore-pattern $exclude"
103204
done
104205
fi
105-
106206
eval $eslint_cmd || :
107207
fi
108208

109-
110-
# Run Bandit (Python SAST) if enabled
111-
if [[ "$INPUT_PYTHON_SAST_ENABLED" == "true" ]]; then
112-
echo "Running Bandit"
113-
bandit_cmd="bandit -r $GITHUB_WORKSPACE -f json -o /tmp/bandit_output.json"
114-
if [[ -n "$BANDIT_EXCLUDE_DIR" ]]; then
115-
bandit_cmd+=" --exclude $BANDIT_EXCLUDE_DIR"
116-
fi
117-
if [[ -n "$BANDIT_RULES" ]]; then
118-
bandit_cmd+=" --skip $BANDIT_RULES"
119-
fi
120-
echo $bandit_cmd
121-
eval $bandit_cmd || :
122-
fi
123-
124-
# Run Gosec (Golang SAST) if enabled
125-
if [[ "$INPUT_GOLANG_SAST_ENABLED" == "true" ]]; then
126-
echo "Running Gosec"
127-
gosec_cmd="gosec -fmt json -out /tmp/gosec_output.json "
128-
if [[ -n "$GOSEC_EXCLUDE_DIR" ]]; then
129-
gosec_cmd+=" -exclude-dir=$GOSEC_EXCLUDE_DIR"
130-
fi
131-
if [[ -n "$GOSEC_RULES" ]]; then
132-
gosec_cmd+=" -severity=$GOSEC_RULES"
133-
fi
134-
gosec_cmd+=" $GITHUB_WORKSPACE/..."
135-
eval $gosec_cmd || :
136-
fi
137-
138-
# Run Trivy on Container Images if enabled
139-
if [[ "$INPUT_TRIVY_IMAGE_ENABLED" == "true" ]]; then
140-
echo "Running Trivy on Container Images"
141-
IFS=',' read -ra DOCKER_IMAGES <<< "${INPUT_DOCKER_IMAGES}"
142-
for image in "${DOCKER_IMAGES[@]}"; do
143-
echo "Scanning image: $image"
144-
trivy image --scanners vuln --format json --output "/tmp/trivy_image_${image//\//_}.json" "$image" || :
145-
done
146-
fi
147-
148-
# Run Trivy on Dockerfiles if enabled
149-
if [[ "$INPUT_TRIVY_DOCKERFILE_ENABLED" == "true" ]]; then
150-
IFS=',' read -ra DOCKERFILES <<< "${INPUT_DOCKERFILES}"
151-
for dockerfile in "${DOCKERFILES[@]}"; do
152-
echo "Scanning Dockerfile: $dockerfile"
153-
trivy config --format json --output "/tmp/trivy_dockerfile_${dockerfile//\//_}.json" "$GITHUB_WORKSPACE/$dockerfile" || :
154-
done
209+
# Move output files (no-op if already in correct place)
210+
# Only cd in GitHub Actions, not local
211+
if [ "$LOCAL_TESTING" != "true" ]; then
212+
cd "$WORKSPACE"
155213
fi
156-
157-
# Run Secret Scanning (Trufflehog) if enabled
158-
if [[ "$INPUT_SECRET_SCANNING_ENABLED" == "true" ]]; then
159-
echo "Running Secret Scanning with Trufflehog"
160-
trufflehog_cmd="trufflehog filesystem "
161-
TRUFFLEHOG_EXCLUDE_FILE=$(mktemp)
162-
if [[ -n "$TRUFFLEHOG_EXCLUDE_DIR" ]]; then
163-
IFS=',' read -ra EXCLUDE_DIRS <<< "$TRUFFLEHOG_EXCLUDE_DIR"
164-
for dir in "${EXCLUDE_DIRS[@]}"; do
165-
echo "$dir" >> "$TRUFFLEHOG_EXCLUDE_FILE"
166-
done
167-
trufflehog_cmd+=" -x $TRUFFLEHOG_EXCLUDE_FILE"
168-
fi
169-
if [[ -n "$TRUFFLEHOG_RULES" ]]; then
170-
trufflehog_cmd+=" --rules $TRUFFLEHOG_RULES"
171-
fi
172-
trufflehog_cmd+=" --no-verification -j $GITHUB_WORKSPACE > /tmp/trufflehog_output.json"
173-
eval $trufflehog_cmd || :
214+
# Run the Python script from the correct directory and path
215+
if [[ -n "$PY_SCRIPT_PATH" ]]; then
216+
FINAL_PY_SCRIPT_PATH="$PY_SCRIPT_PATH"
217+
elif [[ "$DEV_MODE" == "true" ]]; then
218+
FINAL_PY_SCRIPT_PATH="$WORKSPACE/src/socket_external_tools_runner.py"
219+
else
220+
FINAL_PY_SCRIPT_PATH="$WORKSPACE/socket_external_tools_runner.py"
174221
fi
175222

176-
# Execute the custom Python script to process findings
177-
if [ "$LOCAL_TESTING" != "true" ]; then
178-
cd /
179-
fi
180-
mv /tmp/*.json .
181-
if [ "$LOCAL_TESTING" != "true" ]; then
182-
python socket_external_tools_runner.py
223+
if [[ -f "$FINAL_PY_SCRIPT_PATH" ]]; then
224+
python "$FINAL_PY_SCRIPT_PATH"
183225
else
184-
python socket_external_tools_runner.py
226+
echo "Error: Python script not found at $FINAL_PY_SCRIPT_PATH" >&2
227+
exit 1
185228
fi

node_modules/.bin/cli

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)