65 lines
1.8 KiB
Go
65 lines
1.8 KiB
Go
package main
|
||
|
||
import (
|
||
"bytes"
|
||
"encoding/json"
|
||
"io"
|
||
)
|
||
|
||
// exactFields 验证精确字段集合;原文重复键、大小写别名和尾随JSON均拒绝。
|
||
func exactFields(raw json.RawMessage, names ...string) (map[string]json.RawMessage, bool) {
|
||
decoder := json.NewDecoder(bytes.NewReader(raw))
|
||
token, err := decoder.Token()
|
||
if err != nil || token != json.Delim('{') {
|
||
return nil, false
|
||
}
|
||
fields := make(map[string]json.RawMessage, len(names))
|
||
for decoder.More() {
|
||
token, err = decoder.Token()
|
||
if err != nil {
|
||
return nil, false
|
||
}
|
||
key, ok := token.(string)
|
||
if !ok {
|
||
return nil, false
|
||
}
|
||
allowed := false
|
||
for _, name := range names {
|
||
allowed = allowed || name == key
|
||
}
|
||
if !allowed {
|
||
return nil, false
|
||
}
|
||
if _, exists := fields[key]; exists {
|
||
return nil, false
|
||
}
|
||
var value json.RawMessage
|
||
if decoder.Decode(&value) != nil || bytes.Equal(bytes.TrimSpace(value), []byte("null")) {
|
||
return nil, false
|
||
}
|
||
fields[key] = value
|
||
}
|
||
if _, err = decoder.Token(); err != nil {
|
||
return nil, false
|
||
}
|
||
var tail any
|
||
return fields, len(fields) == len(names) && decoder.Decode(&tail) == io.EOF
|
||
}
|
||
func decodePayload(raw json.RawMessage) (workflowPayload, bool) {
|
||
fields, ok := exactFields(raw, "run_id", "sequence")
|
||
var out workflowPayload
|
||
if !ok || json.Unmarshal(fields["run_id"], &out.RunID) != nil || json.Unmarshal(fields["sequence"], &out.Sequence) != nil {
|
||
return out, false
|
||
}
|
||
return out, true
|
||
}
|
||
func matchesOutput(raw json.RawMessage, runID string, sequence int) bool {
|
||
fields, ok := exactFields(raw, "left", "right")
|
||
if !ok {
|
||
return false
|
||
}
|
||
left, ok := decodePayload(fields["left"])
|
||
right, ok2 := decodePayload(fields["right"])
|
||
return ok && ok2 && left == (workflowPayload{RunID: runID, Sequence: sequence}) && right == left
|
||
}
|