Skip to content

Commit 4f9fa2d

Browse files
authored
feat: enhance WebHook reporter to support additional lifecycle events (#6965)
* feat: enhance WebHook reporter to support additional lifecycle events * [MegaLinter] Apply linters fixes --------- Co-authored-by: nvuillam <[email protected]>
1 parent ab798c6 commit 4f9fa2d

File tree

5 files changed

+113
-38
lines changed

5 files changed

+113
-38
lines changed

.trivyignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,8 @@ CVE-2025-66506
176176
CVE-2025-66564
177177
# https://avd.aquasec.com/nvd/cve-2025-68156 : github.com/expr-lang/expr: Expr: Denial of Service via uncontrolled recursion in expression evaluation, not applicable in MegaLinter context
178178
CVE-2025-68156
179+
# https://avd.aquasec.com/nvd/cve-2026-22701 : filelock Time-of-Check-Time-of-Use (TOCTOU) race condition leading to privilege escalation, not applicable in MegaLinter context
180+
CVE-2026-22701
179181
# Dockerfile
180182
DS001
181183
DS002

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ Note: Can be used with `oxsecurity/megalinter@beta` in your GitHub Action mega-l
3131
- Reporters
3232
- Add a link inviting to star MegaLinter
3333
- Display in the console reporter the working directory from which the commands are executed by @bdovaz
34+
- Update WebHook reporter so it can send more events for a better integration with UI
3435

3536
- Doc
3637
- JSON Schema: add default values for file extensions and file names variables + improve descriptions
Lines changed: 16 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
#!/usr/bin/env python3
22
"""
33
Web Hook linter reporter
4-
Post linter results to a Web Hook
4+
Post linter lifecycle events to a Web Hook endpoint
55
"""
66
import logging
77

8-
import requests
98
from megalinter import Reporter, config
10-
from megalinter.utils_reporter import build_linter_reporter_external_result
9+
from megalinter.utils_reporter import (
10+
build_linter_reporter_external_result,
11+
build_linter_reporter_start_message,
12+
post_webhook_message,
13+
)
1114

1215

1316
class WebHookLinterReporter(Reporter):
@@ -18,7 +21,7 @@ class WebHookLinterReporter(Reporter):
1821
web_hook_data: object | None = None
1922

2023
def __init__(self, params=None):
21-
# Deactivate GitHub Status by default
24+
# Deactivate WebHook Linter Reporter by default
2225
self.is_active = False
2326
self.processing_order = 20 # Run after text reporter
2427
super().__init__(params)
@@ -35,39 +38,14 @@ def manage_activation(self):
3538
"You need to define WEBHOOK_REPORTER_URL to use WebHookReporter"
3639
)
3740

38-
# Snd webHook to remote server
41+
# Send message when linter is about to start
42+
def initialize(self):
43+
start_message = build_linter_reporter_start_message(self)
44+
post_webhook_message(self.hook_url, start_message, self, "linter start event")
45+
46+
# Send message when linter is completed
3947
def produce_report(self):
4048
self.web_hook_data = build_linter_reporter_external_result(self)
41-
headers = {
42-
"accept": "application/json",
43-
"content-type": "application/json",
44-
}
45-
if config.exists(self.master.request_id, "WEBHOOK_REPORTER_BEARER_TOKEN"):
46-
headers["authorization"] = (
47-
f"Bearer {config.get(self.master.request_id, 'WEBHOOK_REPORTER_BEARER_TOKEN')}"
48-
)
49-
try:
50-
response = requests.post(
51-
self.hook_url, headers=headers, json=self.web_hook_data
52-
)
53-
if 200 <= response.status_code < 299:
54-
logging.debug(
55-
f"[WebHook Reporter] Successfully posted Web Hook for {self.master.descriptor_id}"
56-
f" with {self.master.linter_name}"
57-
)
58-
else:
59-
logging.warning(
60-
f"[WebHook Reporter] Error posting Status for {self.master.descriptor_id}"
61-
f" with {self.master.linter_name}: {response.status_code}\n"
62-
f"API response: {response.text}"
63-
)
64-
except ConnectionError as e:
65-
logging.warning(
66-
f"[WebHook Reporter] Error posting Web Hook for {self.master.descriptor_id}"
67-
f" with {self.master.linter_name}: Connection error {str(e)}"
68-
)
69-
except Exception as e:
70-
logging.warning(
71-
f"[WebHook Reporter] Error posting Web Hook for {self.master.descriptor_id}"
72-
f" with {self.master.linter_name}: Error {str(e)}"
73-
)
49+
post_webhook_message(
50+
self.hook_url, self.web_hook_data, self, "linter complete event"
51+
)
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Web Hook reporter
4+
Post MegaLinter lifecycle events to a Web Hook endpoint
5+
"""
6+
import logging
7+
8+
from megalinter import Reporter, config
9+
from megalinter.utils_reporter import (
10+
build_reporter_external_result,
11+
build_reporter_start_message,
12+
post_webhook_message,
13+
)
14+
15+
16+
class WebHookReporter(Reporter):
17+
name = "WEBHOOK_REPORTER"
18+
scope = "mega-linter"
19+
20+
hook_url: str | None = None
21+
web_hook_data: object | None = None
22+
23+
def __init__(self, params=None):
24+
# Deactivate WebHook reporter by default
25+
self.is_active = False
26+
self.processing_order = 20 # Run after text reporter
27+
super().__init__(params)
28+
29+
def manage_activation(self):
30+
if config.get(self.master.request_id, "WEBHOOK_REPORTER", "false") == "true":
31+
if config.exists(self.master.request_id, "WEBHOOK_REPORTER_URL"):
32+
self.is_active = True
33+
self.hook_url = config.get(
34+
self.master.request_id, "WEBHOOK_REPORTER_URL"
35+
)
36+
else:
37+
logging.error(
38+
"You need to define WEBHOOK_REPORTER_URL to use WebHookReporter"
39+
)
40+
41+
# Send message when MegaLinter is about to start
42+
def initialize(self):
43+
start_message = build_reporter_start_message(self)
44+
post_webhook_message(
45+
self.hook_url, start_message, self, "MegaLinter start event"
46+
)
47+
48+
# Send message when MegaLinter is completed
49+
def produce_report(self):
50+
self.web_hook_data = build_reporter_external_result(self)
51+
post_webhook_message(
52+
self.hook_url, self.web_hook_data, self, "MegaLinter complete event"
53+
)

megalinter/utils_reporter.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import subprocess
77
import time
88

9+
import requests
910
from megalinter import config, utils
1011
from megalinter.constants import (
1112
DEFAULT_RELEASE,
@@ -466,6 +467,46 @@ def manage_redis_stream(result, redis_stream):
466467
return result
467468

468469

470+
def build_webhook_headers(request_id: str) -> dict:
471+
headers = {
472+
"accept": "application/json",
473+
"content-type": "application/json",
474+
}
475+
if config.exists(request_id, "WEBHOOK_REPORTER_BEARER_TOKEN"):
476+
headers["authorization"] = (
477+
f"Bearer {config.get(request_id, 'WEBHOOK_REPORTER_BEARER_TOKEN')}"
478+
)
479+
return headers
480+
481+
482+
def post_webhook_message(hook_url: str, payload: object, reporter, success_label: str):
483+
context = ""
484+
if reporter.scope == "linter":
485+
context = (
486+
f" for {reporter.master.descriptor_id}"
487+
f" with {reporter.master.linter_name}"
488+
)
489+
try:
490+
response = requests.post(
491+
hook_url,
492+
headers=build_webhook_headers(reporter.master.request_id),
493+
json=payload,
494+
)
495+
if 200 <= response.status_code < 299:
496+
logging.debug(
497+
f"[WebHook Reporter] Successfully posted {success_label}{context}"
498+
)
499+
else:
500+
logging.warning(
501+
f"[WebHook Reporter] Error posting {success_label}{context}: {response.status_code}\n"
502+
f"API response: {response.text}"
503+
)
504+
except requests.exceptions.RequestException as e:
505+
logging.warning(
506+
f"[WebHook Reporter] Error posting {success_label}{context}: {str(e)}"
507+
)
508+
509+
469510
def send_redis_message(reporter_self, message_data):
470511
try:
471512
redis = Redis(

0 commit comments

Comments
 (0)