Add support for the PublishAt field
The purpose of the field is to let users set a custom date when the
report should be published. This overrides the default disclosure flow.
Change-Id: I9e01c3f9dd558dc7d3641fc774dd12b3a5b60967
diff --git a/vulnzybot.go b/vulnzybot.go
index a70ade3..459c7b5 100644
--- a/vulnzybot.go
+++ b/vulnzybot.go
@@ -43,6 +43,10 @@
Status string
// Date that the status was modified
StatusModified time.Time
+ // Whether a PublishAt field exists
+ HasPublishAt bool
+ // Custom date when the vulnerability should be published. Takes precedence over the default disclosure flow
+ PublishAt time.Time
// Date when the vulnerability report was sent to the vendor
Reported time.Time
// Number of days since the reported date when the vulnerability should be automatically disclosed
@@ -51,6 +55,8 @@
DeadlineDuration time.Duration
// Date when the vulnerability report should be published if it has been fixed
PublishDateIfFixed time.Time
+ // Whether DeadlineTime could be calculated or not. If false, the vulnerability should not be published if not fixed
+ HasValidDeadlineTime bool
// Deadline when the vulnerability report should be published if not fixed
DeadlineTime time.Time
HasDoNotPublishLabel bool
@@ -59,7 +65,7 @@
type Action struct {
MustPerform bool
- // |Reason| can be `disclosureDeadline` (the deadline has been exceeded), `fixedDeadline` (the issue has been fixed and |deadlineAfterFixed| has passed)
+ // |Reason| can be `disclosureDeadline` (the deadline has been exceeded), `fixedDeadline` (the issue has been fixed and |deadlineAfterFixed| has passed), `customDisclosureDeadline` (the custom PublishAt date has passed)
Reason string
// |Type| can be `warning` (warning before actually disclosing the report), `disclosure` (the report is actually disclosed)
Type string
@@ -79,6 +85,7 @@
deadlineFound := false
reportedFound := false
+ report.HasPublishAt = false
for _, field := range issue.FieldValues {
if field.FieldName == "Deadline" {
deadlineFound = true
@@ -96,16 +103,24 @@
return nil, fmt.Errorf("Error parsing Reported field: %v")
}
}
- }
- if !deadlineFound {
- return nil, fmt.Errorf("The Deadline field isn't set.")
- }
- if !reportedFound {
- return nil, fmt.Errorf("The Reported field isn't set.")
+
+ if field.FieldName == "PublishAt" {
+ report.PublishAt, err = time.Parse(dateLayout, field.FieldValue)
+ if err != nil {
+ return nil, fmt.Errorf("Error parsing PublishAt field: %v")
+ }
+ report.PublishAt = report.PublishAt.Add(gracePeriod)
+ report.HasPublishAt = true
+ }
}
report.PublishDateIfFixed = report.StatusModified.Add(deadlineAfterFixed + gracePeriod)
- report.DeadlineTime = report.Reported.Add(report.DeadlineDuration + gracePeriod)
+ if reportedFound && deadlineFound {
+ report.HasValidDeadlineTime = true
+ report.DeadlineTime = report.Reported.Add(report.DeadlineDuration + gracePeriod)
+ } else {
+ report.HasValidDeadlineTime = false
+ }
report.HasDoNotPublishLabel = slices.Contains(issue.Labels, doNotPublishLabel)
report.HasRestrictedLabel = slices.Contains(issue.Labels, restrictedLabel)
@@ -124,17 +139,30 @@
// Take into account that warnings are published in advance (according to warningPeriod)
var (
- publishDateIfFixed time.Time
- deadlineTime time.Time
+ customPublishAtDate time.Time
+ publishDateIfFixed time.Time
+ deadlineTime time.Time
)
if isWarning {
+ customPublishAtDate = report.PublishAt.Add(-warningPeriod)
publishDateIfFixed = report.PublishDateIfFixed.Add(-warningPeriod)
deadlineTime = report.DeadlineTime.Add(-warningPeriod)
} else {
+ customPublishAtDate = report.PublishAt
publishDateIfFixed = report.PublishDateIfFixed
deadlineTime = report.DeadlineTime
}
+ if report.HasPublishAt {
+ if (!isWarning && customPublishAtDate.Before(now)) || (isWarning && dateEqual(customPublishAtDate, now)) {
+ return &Action{
+ MustPerform: true,
+ Reason: "customDisclosureDeadline",
+ Type: actionType,
+ }
+ }
+ return &Action{MustPerform: false}
+ }
if report.Status == "Fixed" || report.Status == "Verified" {
if (!isWarning && publishDateIfFixed.Before(now)) || (isWarning && dateEqual(publishDateIfFixed, now)) {
return &Action{
@@ -145,7 +173,7 @@
}
return &Action{MustPerform: false}
}
- if (!isWarning && deadlineTime.Before(now)) || (isWarning && dateEqual(deadlineTime, now)) {
+ if report.HasValidDeadlineTime && (!isWarning && deadlineTime.Before(now)) || (isWarning && dateEqual(deadlineTime, now)) {
return &Action{
MustPerform: true,
Reason: "disclosureDeadline",
@@ -255,6 +283,12 @@
} else {
message = fmt.Sprintf("**The %d-day deadline has been exceeded** -- automatically publishing the vulnerability report.", report.DeadlineDays)
}
+ } else if action.Reason == "customDisclosureDeadline" {
+ if action.Type == "warning" {
+ message = fmt.Sprintf("This report will be published in %d days, as set in the PublishAt field.\n\n_Please unset `PublishAt` or add the `%s` label if you want to stop the automatic disclosure._", warningPeriod/(24*time.Hour), doNotPublishLabel)
+ } else {
+ message = "**The PublishAt date has passed** -- automatically publishing the vulnerability report."
+ }
}
err := publishReport(message, report, action, monorailSvc, ctx)
if err != nil {