mirror of
https://github.com/safedep/pmg.git
synced 2026-08-03 07:24:09 +02:00
refactor: move proxy block message rendering to presentation layer (#364)
* refactor: move proxy block message rendering to presentation layer * refactor: introduce ui.ProxyPresenter with injected advisory source * feat: friendly ecosystem labels in proxy block messages * test: cover ecosystemLabel derivation * fix: address review feedback on block context assertions and empty reference line
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
package ui
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
packagev1 "buf.build/gen/go/safedep/api/protocolbuffers/go/safedep/messages/package/v1"
|
||||
"github.com/safedep/pmg/proxy"
|
||||
)
|
||||
|
||||
// ecosystemLabel maps the ecosystem enum to the label users know the
|
||||
// registry by (npm, pypi, go, ...) instead of the raw enum name.
|
||||
func ecosystemLabel(ecosystem packagev1.Ecosystem) string {
|
||||
return strings.ToLower(strings.TrimPrefix(ecosystem.String(), "ECOSYSTEM_"))
|
||||
}
|
||||
|
||||
// ProxyPresenter composes all user-facing text authored by the proxy layer.
|
||||
// Interceptors return structured decisions; any new proxy-emitted message
|
||||
// belongs here, not in the proxy layer.
|
||||
type ProxyPresenter struct {
|
||||
// Advisory returns the org-configured advisory message appended to
|
||||
// policy block messages. Read at render time so config changes apply
|
||||
// to subsequent blocks. nil means no advisory.
|
||||
Advisory func() string
|
||||
}
|
||||
|
||||
// BlockMessage renders the response body for a blocked proxy request
|
||||
// from the interceptor's structured block decision.
|
||||
func (p ProxyPresenter) BlockMessage(reason proxy.BlockReason, blockCtx *proxy.BlockContext) string {
|
||||
if blockCtx == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
ecosystem := ecosystemLabel(blockCtx.Ecosystem)
|
||||
|
||||
var message string
|
||||
switch reason {
|
||||
case proxy.BlockReasonMalware, proxy.BlockReasonUserDeclined:
|
||||
prefix := "Malicious package blocked"
|
||||
if reason == proxy.BlockReasonUserDeclined {
|
||||
prefix = "Installation blocked by user"
|
||||
}
|
||||
|
||||
message = fmt.Sprintf("%s: %s/%s@%s\n\nReason: %s",
|
||||
prefix, ecosystem, blockCtx.PackageName, blockCtx.PackageVersion, blockCtx.MalwareSummary)
|
||||
if blockCtx.MalwareReferenceURL != "" {
|
||||
message += "\n\nReference: " + blockCtx.MalwareReferenceURL
|
||||
}
|
||||
|
||||
case proxy.BlockReasonConfirmationFailed:
|
||||
// Operational failure rather than a policy decision; the advisory
|
||||
// message is intentionally not appended.
|
||||
return fmt.Sprintf("Failed to get user confirmation for suspicious package %s/%s@%s",
|
||||
ecosystem, blockCtx.PackageName, blockCtx.PackageVersion)
|
||||
|
||||
case proxy.BlockReasonDependencyCooldown:
|
||||
message = fmt.Sprintf("Package blocked by dependency cooldown: %s/%s@%s\n\nPublished %d day(s) ago; cooldown window is %d day(s) (%d remaining).",
|
||||
ecosystem, blockCtx.PackageName, blockCtx.PackageVersion,
|
||||
blockCtx.CooldownDaysAgo, blockCtx.CooldownDays, blockCtx.CooldownDaysLeft)
|
||||
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
|
||||
if p.Advisory != nil {
|
||||
if advisory := p.Advisory(); advisory != "" {
|
||||
message += "\n\n" + advisory
|
||||
}
|
||||
}
|
||||
return message
|
||||
}
|
||||
Reference in New Issue
Block a user