Files
pmg/internal/ui/report_test.go
T
Sahil BansalandGitHub 2d938ea381 feat: advisory message appended to block output (#362)
* docs(specs): add custom block messages and package blocklist spec

* docs(specs): add custom block messages and package blocklist implementation plan

* feat(config): add blocked_packages list and custom block messages

* feat(audit): add package_blocklist_blocked event and blocklist model

* feat(proxy): block blocklisted packages in the policy gate before analysis

* feat(guard): block blocklisted packages before trust skip and analysis

* feat(ui): render blocklist blocks and custom messages, fix silent-mode block output

* feat(proxy): append custom messages to malware and go-cooldown block bodies

* test(proxye2e): cover blocklist enforcement and custom block messages

* docs(specs): remove spec and plan documents

* refactor: drop guard-flow blocklist enforcement and trim docs

Guard mode is being deprecated; the blocklist is enforced in proxy mode
only. Remove the trusted_packages mirroring references outside the docs.

* refactor(config): consolidate blocklist and block message under top-level block section

Replace dependency_cooldown.message, malware.message and blocked_packages
with a single block section: block.message is appended to every block
output regardless of which control blocked, and block.packages is the
package blocklist.

* fix(ui): render block.message as info note with clean spacing

* fix(ui): indent wrapped continuation lines in block reasons and messages

* update config template

* refactor(config): replace block section with top-level advisory_message

Remove the package blocklist (will be implemented as part of policies in
the future) and replace block.message with an optional top-level
advisory_message appended to every block output.

* chore(config): move advisory_message near top-level scalar configs in template
2026-07-09 17:49:00 +05:30

139 lines
3.8 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package ui
import (
"io"
"os"
"strings"
"testing"
packagev1 "buf.build/gen/go/safedep/api/protocolbuffers/go/safedep/messages/package/v1"
"github.com/safedep/pmg/analyzer"
"github.com/safedep/pmg/internal/models"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func captureStdout(t *testing.T, fn func()) string {
t.Helper()
old := os.Stdout
r, w, err := os.Pipe()
require.NoError(t, err)
os.Stdout = w
defer func() { os.Stdout = old }()
fn()
require.NoError(t, w.Close())
out, err := io.ReadAll(r)
require.NoError(t, err)
return string(out)
}
func malwareBlockedData() *ReportData {
data := NewReportData()
data.TotalAnalyzed = 1
data.BlockedCount = 1
data.Outcome = OutcomeBlocked
data.BlockedPackages = []*analyzer.PackageVersionAnalysisResult{
{
PackageVersion: &packagev1.PackageVersion{
Package: &packagev1.Package{Name: "evil", Ecosystem: packagev1.Ecosystem_ECOSYSTEM_NPM},
Version: "1.0.0",
},
Summary: "verified malware",
},
}
return data
}
func TestReportNormalMalwareAdvisoryMessage(t *testing.T) {
withVerbosity(t, VerbosityLevelNormal)
data := malwareBlockedData()
data.AdvisoryMessage = "Contact #security-help"
out := captureStdout(t, func() { Report(data) })
assert.Contains(t, out, "Malicious package blocked")
assert.Contains(t, out, " Contact #security-help")
assert.NotContains(t, out, "\n\n\n", "no double blank lines in block output")
}
func TestReportNormalNoAdvisoryMessageWhenUnset(t *testing.T) {
withVerbosity(t, VerbosityLevelNormal)
out := captureStdout(t, func() { Report(malwareBlockedData()) })
assert.Contains(t, out, "Malicious package blocked")
assert.NotContains(t, out, "Contact #security-help")
}
func TestReportNormalCooldownAdvisoryMessage(t *testing.T) {
withVerbosity(t, VerbosityLevelNormal)
data := NewReportData()
data.TotalAnalyzed = 1
data.BlockedCount = 1
data.Outcome = OutcomeBlocked
data.CooldownBlockedPackages = []models.CooldownBlock{{Name: "fresh", Version: "2.0.0", DaysAgo: 1, DaysLeft: 4, CooldownDays: 5}}
data.AdvisoryMessage = "Request an exemption at go/pmg-exceptions"
out := captureStdout(t, func() { Report(data) })
assert.Contains(t, out, "Dependency cooldown")
assert.Contains(t, out, "Request an exemption at go/pmg-exceptions")
}
func TestReportSilentRendersBlocks(t *testing.T) {
withVerbosity(t, VerbosityLevelSilent)
data := malwareBlockedData()
data.AdvisoryMessage = "Contact #security-help"
out := captureStdout(t, func() { Report(data) })
assert.Contains(t, out, "Malicious package blocked")
assert.Contains(t, out, "Contact #security-help")
}
func TestReportSilentQuietOnSuccess(t *testing.T) {
withVerbosity(t, VerbosityLevelSilent)
data := NewReportData()
data.TotalAnalyzed = 3
out := captureStdout(t, func() { Report(data) })
assert.Empty(t, out)
}
func TestReportSilentCooldownOnlyStaysQuiet(t *testing.T) {
withVerbosity(t, VerbosityLevelSilent)
data := NewReportData()
data.TotalAnalyzed = 1
data.BlockedCount = 1
data.Outcome = OutcomeBlocked
data.CooldownBlockedPackages = []models.CooldownBlock{{Name: "fresh", Version: "2.0.0"}}
out := captureStdout(t, func() { Report(data) })
assert.Empty(t, out)
}
func TestReportVerboseAdvisoryMessage(t *testing.T) {
withVerbosity(t, VerbosityLevelVerbose)
data := malwareBlockedData()
data.AdvisoryMessage = "Contact #security-help"
out := captureStdout(t, func() { Report(data) })
assert.Contains(t, out, "Installation blocked")
assert.Contains(t, out, " Contact #security-help")
}
func TestTermWidthFormatTextIndent(t *testing.T) {
text := strings.Repeat("word ", 40)
out := termWidthFormatTextIndent(text, 20, " ")
lines := strings.Split(out, "\n")
require.Greater(t, len(lines), 1)
for _, line := range lines[1:] {
assert.True(t, strings.HasPrefix(line, " "), "wrapped line must be indented: %q", line)
}
}