Skip to content

Commit b09e41a

Browse files
Update secret-scan-gitleaks.yml
Signed-off-by: LUIZ HAMILTON ROBERTO DA SILVA <[email protected]>
1 parent b770efd commit b09e41a

1 file changed

Lines changed: 115 additions & 44 deletions

File tree

.github/workflows/secret-scan-gitleaks.yml

Lines changed: 115 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -28,21 +28,42 @@ jobs:
2828
OUT_SARIF: "gitleaks.sarif"
2929
OUT_JSON: "gitleaks.json"
3030
OUT_LOG: "gitleaks.log"
31+
OUT_MD: "gitleaks-report.md"
3132
CONFIG_FILE: ".gitleaks.toml"
3233

3334
steps:
3435
- name: Checkout (PR-fast / Main-full)
3536
uses: actions/checkout@v4
3637
with:
37-
# PR: shallow is fine (fast). Main push: full history for maximum coverage.
3838
fetch-depth: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' && 0 || 2 }}
3939

40-
- name: Ensure output folder exists
40+
- name: Ensure output folder exists + baselines
4141
shell: bash
4242
run: |
4343
set -euo pipefail
4444
mkdir -p "${OUT_DIR}"
4545
46+
# Always create JSON baseline (empty array)
47+
echo "[]" > "${OUT_DIR}/${OUT_JSON}"
48+
49+
# Always create SARIF baseline
50+
cat > "${OUT_DIR}/${OUT_SARIF}" << 'EOF'
51+
{
52+
"$schema": "https://json.schemastore.org/sarif-2.1.0.json",
53+
"version": "2.1.0",
54+
"runs": [
55+
{ "tool": { "driver": { "name": "gitleaks", "version": "0" } }, "results": [] }
56+
]
57+
}
58+
EOF
59+
60+
# Always create MD baseline
61+
cat > "${OUT_DIR}/${OUT_MD}" << 'EOF'
62+
# Gitleaks Secret Scan (Report-Only)
63+
64+
Status: Not executed yet.
65+
EOF
66+
4667
- name: Install Gitleaks (pinned)
4768
shell: bash
4869
run: |
@@ -57,18 +78,16 @@ jobs:
5778
if: always()
5879
shell: bash
5980
run: |
60-
# Report-only: never fail the job
6181
set +e
6282
6383
CFG=()
6484
if [ -f "${CONFIG_FILE}" ]; then
6585
CFG=(--config="${CONFIG_FILE}")
6686
fi
6787
68-
# PR: scan working tree only (no history) to keep it fast.
69-
# Main push: full history (because fetch-depth=0).
7088
EXTRA=()
7189
if [ "${{ github.event_name }}" = "pull_request" ]; then
90+
# PR-fast: scan working tree only (no git history)
7291
EXTRA=(--no-git)
7392
fi
7493
@@ -81,14 +100,42 @@ jobs:
81100
--report-path="${OUT_DIR}/${OUT_JSON}" \
82101
2>&1 | tee "${OUT_DIR}/${OUT_LOG}"
83102
84-
# Convert JSON -> SARIF (no re-scan)
103+
exit 0
104+
105+
- name: Build SARIF + Markdown report (from JSON/log)
106+
if: always()
107+
shell: bash
108+
run: |
109+
set -euo pipefail
85110
python3 - << 'PY'
86-
import json, os, sys
87-
out_dir = os.environ["OUT_DIR"]
111+
import json, os, re
112+
113+
out_dir = os.environ["OUT_DIR"]
88114
json_path = os.path.join(out_dir, os.environ["OUT_JSON"])
89-
sarif_path = os.path.join(out_dir, os.environ["OUT_SARIF"])
90-
ver = os.environ.get("GITLEAKS_VERSION","0")
115+
sarif_path= os.path.join(out_dir, os.environ["OUT_SARIF"])
116+
log_path = os.path.join(out_dir, os.environ["OUT_LOG"])
117+
md_path = os.path.join(out_dir, os.environ["OUT_MD"])
118+
ver = os.environ.get("GITLEAKS_VERSION","0")
91119
120+
# Read log (for error context)
121+
log_text = ""
122+
try:
123+
with open(log_path, "r", encoding="utf-8", errors="replace") as f:
124+
log_text = f.read()
125+
except Exception:
126+
pass
127+
128+
# Load findings JSON (fallback to empty list)
129+
findings = []
130+
try:
131+
with open(json_path, "r", encoding="utf-8") as f:
132+
data = json.load(f)
133+
if isinstance(data, list):
134+
findings = data
135+
except Exception:
136+
findings = []
137+
138+
# --- SARIF ---
92139
sarif = {
93140
"$schema": "https://json.schemastore.org/sarif-2.1.0.json",
94141
"version": "2.1.0",
@@ -98,44 +145,68 @@ jobs:
98145
}]
99146
}
100147
101-
try:
102-
with open(json_path, "r", encoding="utf-8") as f:
103-
data = json.load(f)
104-
except Exception:
105-
with open(sarif_path, "w", encoding="utf-8") as f:
106-
json.dump(sarif, f, ensure_ascii=False, indent=2)
107-
sys.exit(0)
108-
109-
if isinstance(data, list):
110-
for item in data:
111-
file_path = item.get("File") or item.get("file") or item.get("Path") or ""
112-
start_line = item.get("StartLine") or item.get("startLine") or item.get("Line") or item.get("line") or 1
113-
rule_id = item.get("RuleID") or item.get("ruleID") or item.get("Rule") or "gitleaks"
114-
desc = item.get("Description") or item.get("description") or "Potential secret detected"
115-
msg = f"{desc} (rule: {rule_id})"
116-
117-
try:
118-
start_line = int(start_line)
119-
except Exception:
120-
start_line = 1
121-
122-
sarif["runs"][0]["results"].append({
123-
"ruleId": str(rule_id),
124-
"level": "error",
125-
"message": {"text": msg},
126-
"locations": [{
127-
"physicalLocation": {
128-
"artifactLocation": {"uri": file_path},
129-
"region": {"startLine": start_line}
130-
}
131-
}]
132-
})
148+
def pick(d, *keys, default=None):
149+
for k in keys:
150+
if k in d and d[k] is not None:
151+
return d[k]
152+
return default
153+
154+
for item in findings:
155+
file_path = pick(item, "File", "file", "Path", "path", default="")
156+
start_line = pick(item, "StartLine", "startLine", "Line", "line", default=1)
157+
rule_id = pick(item, "RuleID", "ruleID", "Rule", "rule", default="gitleaks")
158+
desc = pick(item, "Description", "description", default="Potential secret detected")
159+
160+
try:
161+
start_line = int(start_line)
162+
except Exception:
163+
start_line = 1
164+
165+
sarif["runs"][0]["results"].append({
166+
"ruleId": str(rule_id),
167+
"level": "error",
168+
"message": {"text": f"{desc} (rule: {rule_id})"},
169+
"locations": [{
170+
"physicalLocation": {
171+
"artifactLocation": {"uri": file_path},
172+
"region": {"startLine": start_line}
173+
}
174+
}]
175+
})
133176
134177
with open(sarif_path, "w", encoding="utf-8") as f:
135178
json.dump(sarif, f, ensure_ascii=False, indent=2)
136-
PY
137179
138-
exit 0
180+
# --- Markdown report ---
181+
status = "OK"
182+
# Detect config failure or fatal error in log
183+
if re.search(r"\bFTL\b|\bFailed to load config\b", log_text, re.IGNORECASE):
184+
status = "ERROR (see log)"
185+
186+
md = []
187+
md.append("# Gitleaks Secret Scan (Report-Only)")
188+
md.append("")
189+
md.append(f"**Status:** {status}")
190+
md.append(f"**Mode:** {'PR-fast (working tree only)' if os.environ.get('GITHUB_EVENT_NAME','')=='pull_request' else 'Main (full history)'}")
191+
md.append(f"**Findings:** {len(findings)}")
192+
md.append("")
193+
if len(findings) == 0:
194+
md.append("No secrets detected (or scan produced no findings).")
195+
else:
196+
# Top rule counts
197+
counts = {}
198+
for x in findings:
199+
rid = pick(x, "RuleID", "ruleID", "Rule", "rule", default="gitleaks")
200+
counts[rid] = counts.get(rid, 0) + 1
201+
md.append("## Top Rules")
202+
for rid, c in sorted(counts.items(), key=lambda kv: kv[1], reverse=True)[:15]:
203+
md.append(f"- `{rid}`: {c}")
204+
md.append("")
205+
md.append("> Artifacts: `gitleaks.json`, `gitleaks.sarif`, `gitleaks.log`")
206+
207+
with open(md_path, "w", encoding="utf-8") as f:
208+
f.write("\n".join(md) + "\n")
209+
PY
139210
140211
- name: Upload artifacts (reports)
141212
if: always()

0 commit comments

Comments
 (0)