Skip to content

Commit 863909c

Browse files
authored
Fixing eslint logic (#20)
1 parent 61a945b commit 863909c

10 files changed

Lines changed: 209 additions & 79 deletions

File tree

README.md

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ jobs:
2222
uses: actions/checkout@v4.2.1
2323

2424
- name: Run Security Scan and Comment Action
25-
uses: SocketDev/security-wrapper@1.0.16
25+
uses: SocketDev/security-wrapper@1.0.17
2626
with:
2727
github_token: ${{ secrets.GITHUB_TOKEN }}
2828

@@ -45,6 +45,21 @@ jobs:
4545
bandit_rules: "B101,B102,B105,B106,B107,B110,B603,B605,B607"
4646
gosec_rules: "medium"
4747
gosec_exclude_dir: "tests,migrations,tests,test,.venv,venv"
48+
eslint_rules: >
49+
security/detect-eval-with-expression,
50+
security/detect-non-literal-require,
51+
security/detect-non-literal-fs-filename,
52+
security/detect-buffer-noassert,
53+
security/detect-new-buffer,
54+
security/detect-unsafe-regex,
55+
security/detect-disable-mustache-escape,
56+
security/detect-no-csrf-before-method-override,
57+
security/detect-pseudoRandomBytes,
58+
security/detect-possible-timing-attacks,
59+
security/detect-bidi-characters,
60+
security/detect-child-process,
61+
security/detect-non-literal-regexp,
62+
security/detect-object-injection
4863
4964
# Log forwarding
5065
sumo_logic_enabled: true

entrypoint.sh

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

16+
1617
# Run ESLint (JavaScript SAST) if enabled
17-
if [[ "$INPUT_JAVASCRIPT_SAST_ENABLED" == "true" ]]; then
18-
echo "Running ESLint"
19-
ESLINT_EXCLUDE_DIR=${INPUT_ESLINT_EXCLUDE_DIR:-}
20-
ESLINT_RULES=${INPUT_ESLINT_RULES:-}
21-
22-
eslint_cmd="npx eslint $GITHUB_WORKSPACE --ext .js,.jsx --format json --output-file /tmp/eslint_output.json"
23-
if [[ -n "$ESLINT_EXCLUDE_DIR" ]]; then
24-
eslint_cmd+=" --ignore-pattern $ESLINT_EXCLUDE_DIR"
25-
fi
26-
if [[ -n "$ESLINT_RULES" ]]; then
27-
eslint_cmd+=" --rule \"$ESLINT_RULES\""
28-
fi
29-
eval $eslint_cmd || :
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:-}
22+
23+
if [[ -z "$ESLINT_RULES" ]]; then
24+
echo "Using default ESLint rules"
25+
ESLINT_RULES=$(cat <<'EOF'
26+
security/detect-eval-with-expression,
27+
security/detect-non-literal-require,
28+
security/detect-non-literal-fs-filename,
29+
security/detect-buffer-noassert,
30+
security/detect-new-buffer,
31+
security/detect-unsafe-regex,
32+
security/detect-disable-mustache-escape,
33+
security/detect-no-csrf-before-method-override,
34+
security/detect-pseudoRandomBytes,
35+
security/detect-possible-timing-attacks,
36+
security/detect-bidi-characters,
37+
security/detect-child-process,
38+
security/detect-non-literal-regexp,
39+
security/detect-object-injection,
40+
@typescript-eslint/no-implied-eval,
41+
@typescript-eslint/no-throw-literal,
42+
@typescript-eslint/no-misused-promises,
43+
@typescript-eslint/no-unsafe-argument,
44+
@typescript-eslint/no-unsafe-assignment,
45+
@typescript-eslint/no-unsafe-call,
46+
@typescript-eslint/no-unsafe-member-access,
47+
@typescript-eslint/no-unsafe-return,
48+
@typescript-eslint/ban-ts-comment,
49+
@typescript-eslint/no-explicit-any,
50+
@typescript-eslint/explicit-module-boundary-types,
51+
@typescript-eslint/no-floating-promises,
52+
@typescript-eslint/no-for-in-array,
53+
@typescript-eslint/no-misused-new,
54+
@typescript-eslint/no-non-null-asserted-optional-chain,
55+
@typescript-eslint/no-non-null-assertion,
56+
@typescript-eslint/no-unnecessary-type-assertion,
57+
@typescript-eslint/prefer-optional-chain,
58+
@typescript-eslint/prefer-nullish-coalescing,
59+
@typescript-eslint/restrict-plus-operands,
60+
@typescript-eslint/restrict-template-expressions,
61+
@typescript-eslint/require-await,
62+
@typescript-eslint/unbound-method,
63+
@typescript-eslint/array-type,
64+
@typescript-eslint/ban-types,
65+
@typescript-eslint/consistent-type-assertions,
66+
@typescript-eslint/consistent-type-definitions,
67+
@typescript-eslint/explicit-function-return-type,
68+
@typescript-eslint/no-empty-interface,
69+
@typescript-eslint/no-inferrable-types,
70+
@typescript-eslint/no-invalid-void-type,
71+
@typescript-eslint/no-redeclare,
72+
@typescript-eslint/no-shadow,
73+
@typescript-eslint/no-unused-vars,
74+
@typescript-eslint/no-use-before-define,
75+
@typescript-eslint/prefer-as-const
76+
EOF
77+
)
78+
fi
79+
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/,$//')
82+
83+
if [[ ! -f "$WORKSPACE/eslint.config.mjs" ]]; then
84+
echo "Adding fallback ESLint config"
85+
cat <<EOF > "$WORKSPACE/eslint.config.mjs"
86+
export default [
87+
{
88+
files: ['**/*.js', '**/*.jsx', '**/*.ts', '**/*.tsx'],
89+
rules: {
90+
$ESLINT_RULES_JSON
91+
},
92+
},
93+
];
94+
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"
98+
99+
if [[ -n "$ESLINT_EXCLUDE_DIR" ]]; then
100+
IFS=',' read -ra EXCLUDES <<< "$ESLINT_EXCLUDE_DIR"
101+
for exclude in "${EXCLUDES[@]}"; do
102+
eslint_cmd+=" --ignore-pattern $exclude"
103+
done
104+
fi
105+
106+
eval $eslint_cmd || :
30107
fi
31108

109+
32110
# Run Bandit (Python SAST) if enabled
33111
if [[ "$INPUT_PYTHON_SAST_ENABLED" == "true" ]]; then
34112
echo "Running Bandit"
@@ -39,6 +117,7 @@ if [[ "$INPUT_PYTHON_SAST_ENABLED" == "true" ]]; then
39117
if [[ -n "$BANDIT_RULES" ]]; then
40118
bandit_cmd+=" --skip $BANDIT_RULES"
41119
fi
120+
echo $bandit_cmd
42121
eval $bandit_cmd || :
43122
fi
44123

@@ -95,6 +174,12 @@ if [[ "$INPUT_SECRET_SCANNING_ENABLED" == "true" ]]; then
95174
fi
96175

97176
# Execute the custom Python script to process findings
98-
cd /
177+
if [ "$LOCAL_TESTING" != "true" ]; then
178+
cd /
179+
fi
99180
mv /tmp/*.json .
100-
python socket_external_tools_runner.py
181+
if [ "$LOCAL_TESTING" != "true" ]; then
182+
python socket_external_tools_runner.py
183+
else
184+
python socket_external_tools_runner.py
185+
fi

eslint.config.mjs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export default [
2+
{
3+
files: ['**/*.js', '**/*.jsx'],
4+
rules: {},
5+
},
6+
];

src/core/connectors/bandit/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ def process_output(cls, data: dict, cwd: str, plugin_name: str = "Bandit") -> di
2121
results = data.get(cls.result_key, [])
2222
for entry in results:
2323
test_result = cls.result_class(**entry, cwd=cwd)
24+
if test_result.issue_severity.lower() not in cls.default_severities:
25+
continue
2426
test_result.plugin_name = plugin_name
2527
test_result.file = test_result.filename # Ensure compatibility with create_output()
2628

src/core/connectors/classes.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,14 @@ def set_url(self):
2828
def set_timestamp(self):
2929
self.timestamp = datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M:%S,%f")[:-3] + " +0000"
3030

31+
# Add a method to convert the object to a dictionary
32+
def to_json(self):
33+
"""Convert the object to a dictionary for JSON serialization."""
34+
return self.__dict__
35+
36+
# Ensure the object string representation works well with JSON
3137
def __str__(self):
32-
return json.dumps(self.__dict__)
38+
return json.dumps(self.to_json())
3339

3440

3541
class BanditTestResult(BaseTestResult):
@@ -104,6 +110,7 @@ def __init__(self, **kwargs):
104110
self.timestamp = ""
105111
self.plugin_name = "ESLint"
106112
self.rule_id = ""
113+
self.severity = ""
107114
super().__init__(**kwargs)
108115

109116
def set_url(self):

src/core/connectors/eslint/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,8 @@ def process_output(cls, data: list, cwd: str, plugin_name: str = "ESLint", retur
103103
issue_text=issue_text,
104104
cwd=cwd
105105
)
106+
if test_result.severity not in cls.default_severities:
107+
continue
106108
test_result.plugin_name = plugin_name
107109
test_name = cls.get_test_name(test_result)
108110
test_result.use_custom = True

src/core/connectors/gosec/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ def process_output(cls, data: dict, cwd: str, plugin_name: str = "Gosec") -> dic
2121
results = data.get(cls.result_key, [])
2222
for entry in results:
2323
test_result = cls.result_class(**entry, cwd=cwd)
24+
if test_result.severity.lower() not in cls.default_severities:
25+
continue
2426
test_result.plugin_name = plugin_name
2527
test_result.file = test_result.file # Ensure compatibility with create_output()
2628

src/core/connectors/trufflehog/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ def process_output(cls, data: dict, cwd: str, plugin_name: str = "Trufflehog", s
3030
continue
3131

3232
test_result = cls.result_class(**entry, cwd=cwd)
33-
33+
if test_result.severity.lower() not in cls.default_severities:
34+
continue
3435
test_result.plugin_name = plugin_name
3536
test_result.file = entry.get("SourceMetadata", {}).get("Data", {}).get("Filesystem", {}).get("file", "")
3637
test_result.file = test_result.file.replace(cwd, '').lstrip("/")

src/core/plugins/microsoft_sentinel/sentinel.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -200,13 +200,18 @@ def normalize_events(events: list, plugin_name: str):
200200
"""Detects event type and normalizes them for Sentinel ingestion."""
201201
formatted_events = []
202202

203-
for event in events:
204-
if isinstance(event, str):
203+
for event_details in events:
204+
if type(event_details) == str:
205205
try:
206-
event = json.loads(event) # Convert from string if necessary
206+
event = json.loads(event_details)
207+
if type(event) == str:
208+
event = json.loads(event)
207209
except json.JSONDecodeError:
208-
print(f"Skipping invalid event: {event}")
210+
print(f"Skipping invalid event: {event_details}")
209211
continue # Skip invalid JSON entries
212+
else:
213+
event = event_details
214+
210215

211216
if "plugin_name" in event:
212217
if "bandit" in plugin_name.lower():

src/socket_external_tools_runner.py

Lines changed: 62 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import json
22
import logging
3+
import argparse
34
import os
45
import glob
56
import inspect
@@ -67,66 +68,70 @@ def consolidate_trivy_results(pattern: str) -> dict:
6768
"eslint": "ESLint"
6869
}
6970

70-
# Load results
71-
results = {
72-
"bandit": load_json("bandit_output.json", "Bandit"),
73-
"gosec": load_json("gosec_output.json", "Gosec"),
74-
"trufflehog": load_json("trufflehog_output.json", "Trufflehog"),
75-
"trivy_image": consolidate_trivy_results("trivy_image_*.json"),
76-
"trivy_dockerfile": consolidate_trivy_results("trivy_dockerfile_*.json"),
77-
"eslint": load_json("eslint_output.json", "ESLint")
78-
}
71+
def main():
72+
# Load results
73+
results = {
74+
"bandit": load_json("bandit_output.json", "Bandit"),
75+
"gosec": load_json("gosec_output.json", "Gosec"),
76+
"trufflehog": load_json("trufflehog_output.json", "Trufflehog"),
77+
"trivy_image": consolidate_trivy_results("trivy_image_*.json"),
78+
"trivy_dockerfile": consolidate_trivy_results("trivy_dockerfile_*.json"),
79+
"eslint": load_json("eslint_output.json", "ESLint")
80+
}
7981

80-
if any(results.values()):
81-
if not SCM_DISABLED:
82-
scm = SCM()
83-
tool_outputs = {}
84-
tool_events = {}
82+
if any(results.values()):
83+
if not SCM_DISABLED:
84+
scm = SCM()
85+
tool_outputs = {}
86+
tool_events = {}
8587

86-
for key, data in results.items():
87-
if data:
88-
tool_marker = marker.replace("REPLACE_ME", TOOL_NAMES[key])
89-
tool_class = TOOL_CLASSES[key]
90-
if SEVERITIES:
91-
tool_class.default_severities = SEVERITIES
88+
for key, data in results.items():
89+
if data:
90+
tool_marker = marker.replace("REPLACE_ME", TOOL_NAMES[key])
91+
tool_class = TOOL_CLASSES[key]
92+
if SEVERITIES:
93+
tool_class.default_severities = SEVERITIES
9294

93-
supports_show_unverified = "show_unverified" in inspect.signature(tool_class.process_output).parameters
94-
if supports_show_unverified:
95-
show_unverified = os.getenv("INPUT_TRUFFLEHOG_SHOW_UNVERIFIED", "false").lower() == "true"
96-
tool_outputs[key], tool_results = tool_class.create_output(
97-
data,
98-
tool_marker,
99-
scm.github.repo,
100-
scm.github.commit,
101-
scm.github.cwd,
102-
show_unverified=show_unverified
103-
)
104-
else:
105-
tool_outputs[key], tool_results = tool_class.create_output(
106-
data, tool_marker, scm.github.repo, scm.github.commit, scm.github.cwd
107-
)
108-
tool_events[key] = tool_outputs[key].get("events", [])
109-
if tool_events[key]:
110-
scm.github.post_comment(TOOL_NAMES[key], tool_marker, tool_results)
95+
supports_show_unverified = "show_unverified" in inspect.signature(tool_class.process_output).parameters
96+
if supports_show_unverified:
97+
show_unverified = os.getenv("INPUT_TRUFFLEHOG_SHOW_UNVERIFIED", "false").lower() == "true"
98+
tool_outputs[key], tool_results = tool_class.create_output(
99+
data,
100+
tool_marker,
101+
scm.github.repo,
102+
scm.github.commit,
103+
scm.github.cwd,
104+
show_unverified=show_unverified
105+
)
106+
else:
107+
tool_outputs[key], tool_results = tool_class.create_output(
108+
data, tool_marker, scm.github.repo, scm.github.commit, scm.github.cwd
109+
)
110+
tool_events[key] = tool_outputs[key].get("events", [])
111+
if tool_events[key]:
112+
scm.github.post_comment(TOOL_NAMES[key], tool_marker, tool_results)
111113

112-
print("Issues detected with Security Tools. Please check PR comments")
113-
else:
114-
tool_events = {
115-
key: TOOL_CLASSES[key].process_output(data, GIT_DIR, TOOL_NAMES[key])
116-
for key, data in results.items() if data
117-
}
114+
print("Issues detected with Security Tools. Please check PR comments")
115+
else:
116+
tool_events = {
117+
key: TOOL_CLASSES[key].process_output(data, GIT_DIR, TOOL_NAMES[key])
118+
for key, data in results.items() if data
119+
}
118120

119-
if sumo_client:
120-
print("Issues detected with Security Tools. Please check Sumologic Events")
121-
for key, events in tool_events.items():
122-
print(errors) if (errors := sumo_client.send_events(events.get("events"), "../" + key + "_output.json")) else []
121+
if sumo_client:
122+
print("Issues detected with Security Tools. Please check Sumologic Events")
123+
for key, events in tool_events.items():
124+
print(errors) if (errors := sumo_client.send_events(events.get("events"), "../" + key + "_output.json")) else []
123125

124-
if ms_sentinel:
125-
print("Issues detected with Security Tools. Please check Microsoft Sentinel Events")
126-
for key, events in tool_events.items():
127-
sentinel_name = f"SocketSecurityTools{TOOL_NAMES[key]}"
128-
formatted_events = [json.dumps(event) for event in events.get("events", [])]
129-
print(errors) if (errors := ms_sentinel.send_events(formatted_events, sentinel_name)) else []
130-
exit(1)
131-
else:
132-
print("No issues detected with Socket Security Tools")
126+
if ms_sentinel:
127+
print("Issues detected with Security Tools. Please check Microsoft Sentinel Events")
128+
for key, events in tool_events.items():
129+
sentinel_name = f"SocketSecurityTools{TOOL_NAMES[key]}"
130+
formatted_events = [json.dumps(event, default=lambda o: o.to_json()) for event in events.get("events", [])]
131+
print(errors) if (errors := ms_sentinel.send_events(formatted_events, sentinel_name)) else []
132+
exit(1)
133+
else:
134+
print("No issues detected with Socket Security Tools")
135+
136+
if __name__ == "__main__":
137+
main()

0 commit comments

Comments
 (0)