feat(slack): Job Status Notification - #132
Merged
Merged
Conversation
Signed-off-by: Julio Jimenez <julio@julioj.com>
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
Contributor
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Short secrets and untrusted response tokens can still reach Slack-related output, and the action-level job-link handoff remains untested.
Review effort: Balanced
Findings: 2
Open (4)
What changed in this PR
Adds optional, best-effort Slack notifications for ClickBOM run outcomes, including job context, processing summary, failure details, and deep links.
Changes:
- Adds Slack webhook validation, delivery, retries, payload formatting, and secret redaction.
- Integrates notifications into successful, failed, and configuration-error execution paths.
- Adds action inputs, documentation, version metadata, and extensive tests.
| File | Description |
|---|---|
README.md |
Documents Slack inputs and usage. |
internal/validation/sanitize.go |
Validates Slack webhook URLs. |
internal/validation/sanitize_test.go |
Tests webhook validation. |
internal/notify/slack.go |
Implements Slack notification delivery. |
internal/notify/slack_test.go |
Tests payloads, retries, and redaction. |
internal/config/config.go |
Adds Slack and sensitive-value configuration. |
internal/config/config_test.go |
Tests configuration and secret collection. |
Dockerfile |
Updates version labels. |
cmd/clickbom/main.go |
Reports execution outcomes to Slack. |
cmd/clickbom/main_test.go |
Tests summaries and notification orchestration. |
CLAUDE.md |
Documents notification architecture. |
action.yml |
Exposes Slack and job-link inputs. |
.github/workflows/tests.yml |
Adds runtime checks and optional E2E notification. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Comment on lines
+326
to
+329
| case slackTokenRE.MatchString(s): | ||
| return ": " + s | ||
| default: | ||
| return " (response body withheld)" |
Comment on lines
+715
to
+716
| if utf8.RuneCountInString(v) < minSecretRunes || seen[v] { | ||
| return |
Comment on lines
+285
to
+288
| func SanitizeSlackWebhookURL(raw string) (string, error) { | ||
| cleaned := strings.TrimSpace(raw) | ||
| if cleaned == "" || strings.IndexFunc(cleaned, unicode.IsControl) >= 0 { | ||
| return "", errInvalidSlackWebhookURL |
| job-check-run-id: | ||
| description: 'Id of the running job, used only to link the Slack message to the job. Leave the default: job.check_run_id is evaluated here because a Docker action can only see the inputs context. Empty on GitHub Enterprise Server releases without job.check_run_id, in which case the message links to the run.' | ||
| required: false | ||
| default: '${{ job.check_run_id }}' |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Summary
Adds an optional Slack notification to the action. When
slack-webhook-urlis set, every run posts one message, green or red, saying what ran and how it ended, with a link to the job. Nothing changes for consumers that do not set the input.The message carries the repository, workflow, job key and step id that ran ClickBOM, the trigger (event, branch, short SHA, actor), the SBOM source, the S3 object written, the ClickHouse database and table when configured, the duration, and a link. On failure it adds the first line of the error, redacted.
New inputs
slack-webhook-urljob-check-run-id${{ job.check_run_id }}. Leave it alone; it exists only so the message can deep-link to the job.The job link needs the job's check run id, which GitHub exposes only in the expression context. A Docker action's
runs.envmay referenceinputsalone, but an input default may referencejob, so the default carries the id into the container asCLICKBOM_JOB_CHECK_RUN_ID. Servers withoutjob.check_run_id(older GHES) yield an empty value and the message links to the run instead.Security properties
SanitizeSlackWebhookURLaccepts onlyhttps://hooks.slack.com/services/...(and GovSlack'shooks.slack-gov.com), fails closed on control characters, credentials, ports, query strings, fragments and path traversal, and never echoes the rejected value. Workflow Builder trigger URLs are refused because they only take flat key/value payloads.*url.Erroris unwrapped before any error is returned, the HTTP client refuses redirects, and only Slack-style response tokens (invalid_payload,no_service) are quoted, never arbitrary bodies.Retry-Afterhonoured up to 30 s, the whole notification bounded to two minutes, panics recovered. A delivery failure logs a warning and never changes the exit status. Configuration failures are reported too when the webhook itself is valid.Changes
internal/notify(new):SlackNotifier, run context from the runner's default env vars, payload builder, scrubbing and redaction.internal/validation:SanitizeSlackWebhookURL.internal/config:SlackWebhookURL, the sensitive-variable list,Secrets,SecretsFromEnv,SlackWebhookURLFromEnv.cmd/clickbom:runnow wrapsexecuteand reports the outcome;buildSummaryandsecretsForRedaction.action.yml,README.md,CLAUDE.md: inputs, docs, architecture notes..github/workflows/tests.yml: Docker smoke step proving the shipped image withholds a rejected webhook value; optionalTEST_SLACK_WEBHOOK_URLon the E2E job.Dockerfile: version labels set to 2.1.0 for the upcoming release.Testing
go test -race ./...,golangci-lint run, gofmt and gocyclo pass. The unit tests also pass under the integration job's environment (CLICKHOUSE_PASSWORD=clickbom,GITHUB_*set) and with-shuffle=on.S3_BUCKET is required, and an invalid webhook is reported without its value.job.check_run_iddefault in a real workflow. SettingTEST_SLACK_WEBHOOK_URLon this repository makes the E2E job post one message per push.Follow-ups
v2.1.0and bump theClickHouse/sbomworkflows fromv2.0.0.slack-notify-on: always|failureswitch if success messages are too noisy.stripURL, so they can reach the job log. The notifier's scrubber removes them from Slack messages.