aboutsummaryrefslogtreecommitdiff
path: root/internal/cpe/cpe.go
diff options
context:
space:
mode:
authorPaul Duncan <pabs@pablotron.org>2022-02-02 04:08:55 -0500
committerPaul Duncan <pabs@pablotron.org>2022-02-02 04:08:55 -0500
commitb6496c2d20904e665116133a9e9bf9ae6e3b45b8 (patch)
tree72ca645269e545e494e1d6d91145134584abc673 /internal/cpe/cpe.go
parent1b9eb2eddf322560c37efa2ef2eb63d5ab661be8 (diff)
downloadcvez-b6496c2d20904e665116133a9e9bf9ae6e3b45b8.tar.bz2
cvez-b6496c2d20904e665116133a9e9bf9ae6e3b45b8.zip
add internal/cpe
Diffstat (limited to 'internal/cpe/cpe.go')
-rw-r--r--internal/cpe/cpe.go94
1 files changed, 94 insertions, 0 deletions
diff --git a/internal/cpe/cpe.go b/internal/cpe/cpe.go
new file mode 100644
index 0000000..73930e8
--- /dev/null
+++ b/internal/cpe/cpe.go
@@ -0,0 +1,94 @@
+// CPE 2.3 formatted string parser.
+//
+// Source: NISTIR 7605, figure 6-3:
+// https://nvlpubs.nist.gov/nistpubs/Legacy/IR/nistir7695.pdf
+package cpe
+
+import (
+ "fmt"
+)
+
+//go:generate stringer -linecomment -type=tokenType
+
+// token type
+type tokenType byte
+
+const (
+ anyToken tokenType = iota // any
+ naToken // na
+ valToken // val
+)
+
+// token
+type token struct {
+ Type tokenType // token type
+ Val string // token value
+}
+
+// parse buffer into token.
+func newToken(val []byte) token {
+ if len(val) > 0 {
+ switch val[0] {
+ case '*':
+ return token { Type: anyToken }
+ case '-':
+ return token { Type: naToken }
+ default:
+ return token { Type: valToken, Val: string(val) }
+ }
+ } else {
+ // empty value
+ return token { Type: valToken, Val: "" }
+ }
+}
+
+// Parse buffer into slice of tokens.
+func tokenize(buf []byte) ([]token, error) {
+ // build result
+ var r []token
+
+ // current token and escape state
+ var curr []byte
+ esc := false
+
+ // build result
+ for _, b := range(buf) {
+ if esc {
+ switch b {
+ // valid escaped characters
+ case '\\', '*', '-', '!', '"', '#', '$', '%', '&', '\'', '(', ')',
+ '+', ',', '/', ':', ';', '<', '=', '>', '@', '[', ']', '^', '`',
+ '{', '|', '}', '~':
+ curr = append(curr, b)
+ esc = false
+ default:
+ return r, fmt.Errorf("invalid escape byte: 0x%02x", b)
+ }
+ } else {
+ switch b {
+ case '\\':
+ esc = true
+ case ':':
+ // push token, clear buffer
+ r = append(r, newToken(curr))
+ curr = nil
+ case 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
+ 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x',
+ 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+ '-', '.', '_', '*', '?':
+ curr = append(curr, b)
+ default:
+ return r, fmt.Errorf("invalid byte: 0x%02x", b)
+ }
+ }
+ }
+
+ if len(curr) > 0 {
+ // push token, clear buffer
+ r = append(r, newToken(curr))
+ curr = nil
+ }
+
+ // return success
+ return r, nil
+}