Harden NWS key message parsing

This commit is contained in:
2026-08-03 00:19:48 +00:00
parent 9a09454621
commit 6943a5ebc9
2 changed files with 259 additions and 5 deletions

View File

@@ -594,6 +594,18 @@ func parseForecastDiscussionKeyMessages(body []string) []string {
if len(body) == 0 {
return nil
}
hasMarkers := false
for _, raw := range body {
line := strings.TrimSpace(raw)
if line == "" {
continue
}
if _, ok := stripForecastDiscussionKeyMessageMarker(line); ok {
hasMarkers = true
break
}
}
var messages []string
var current strings.Builder
@@ -605,15 +617,21 @@ func parseForecastDiscussionKeyMessages(body []string) []string {
current.Reset()
}
seenMarker := false
for _, raw := range body {
line := strings.TrimSpace(raw)
if line == "" {
if !hasMarkers || !seenMarker {
flush()
}
continue
}
if strings.HasPrefix(line, "-") {
if stripped, ok := stripForecastDiscussionKeyMessageMarker(line); ok {
flush()
line = strings.TrimSpace(strings.TrimPrefix(line, "-"))
current.WriteString(line)
seenMarker = true
line = stripped
}
if line == "" {
continue
}
if current.Len() > 0 {
@@ -690,11 +708,75 @@ func isForecastDiscussionIssuedAtLine(line string) bool {
func isForecastDiscussionKeyMessageMetadataLine(line string) bool {
line = strings.TrimSpace(line)
return hasForecastDiscussionASCIIPrefix(line, "Issued at") || hasForecastDiscussionASCIIPrefix(line, "Updated at")
for _, label := range []string{"Issued at", "Updated at"} {
if !hasForecastDiscussionASCIIPrefix(line, label) || len(line) == len(label) || !isForecastDiscussionHorizontalWhitespace(line[len(label)]) {
continue
}
if _, err := parseForecastDiscussionIssueTime(strings.TrimSpace(line[len(label):])); err == nil {
return true
}
}
return false
}
func stripForecastDiscussionKeyMessageMarker(line string) (string, bool) {
if line == "" {
return "", false
}
if line[0] == '-' || line[0] == '*' {
content := line[1:]
hadWhitespace := len(content) > 0 && isForecastDiscussionHorizontalWhitespace(content[0])
content = strings.TrimLeft(content, " \t")
if hadWhitespace {
if stripped, ok := stripForecastDiscussionKeyMessageNumericMarker(content); ok {
content = stripped
}
}
return content, true
}
return stripForecastDiscussionKeyMessageNumericMarker(line)
}
func stripForecastDiscussionKeyMessageNumericMarker(line string) (string, bool) {
digitEnd := 0
for digitEnd < len(line) && line[digitEnd] >= '0' && line[digitEnd] <= '9' {
digitEnd++
}
if digitEnd == 0 || digitEnd == len(line) || (line[digitEnd] != ')' && line[digitEnd] != '.') {
return "", false
}
markerEnd := digitEnd + 1
if markerEnd < len(line) && !isForecastDiscussionHorizontalWhitespace(line[markerEnd]) {
return "", false
}
value, err := strconv.ParseUint(line[:digitEnd], 10, 0)
if err != nil || value == 0 {
return "", false
}
return strings.TrimLeft(line[markerEnd:], " \t"), true
}
func hasForecastDiscussionASCIIPrefix(line, prefix string) bool {
return len(line) >= len(prefix) && strings.EqualFold(line[:len(prefix)], prefix)
if len(line) < len(prefix) {
return false
}
for i := range prefix {
actual := line[i]
if actual >= 'A' && actual <= 'Z' {
actual += 'a' - 'A'
}
expected := prefix[i]
if expected >= 'A' && expected <= 'Z' {
expected += 'a' - 'A'
}
if actual != expected {
return false
}
}
return true
}
func trimBlankLines(lines []string) []string {