-
-
Notifications
You must be signed in to change notification settings - Fork 102
Expand file tree
/
Copy pathjson_pointer.go
More file actions
143 lines (120 loc) · 2.84 KB
/
Copy pathjson_pointer.go
File metadata and controls
143 lines (120 loc) · 2.84 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
package vulcain
import (
"strings"
"github.com/dunglas/httpsfv"
)
// node represents a node of a JSON document
type node struct {
preload bool
preloadParams []*httpsfv.Params
fields bool
fieldsParams []*httpsfv.Params
path string
parent *node
children []*node
}
// _type is the type of operation to apply, can be Preload or Fields
type _type int
const (
preload _type = iota
fields
)
// importPointers imports JSON pointers in the tree
func (n *node) importPointers(t _type, pointers httpsfv.List) {
for _, member := range pointers {
// Ignore invalid value
member, ok := member.(httpsfv.Item)
if !ok {
continue
}
pointer, ok := member.Value.(string)
if !ok {
continue
}
pointer = strings.Trim(pointer, "/")
if pointer != "" {
partsToTree(t, strings.Split(pointer, "/"), n, member.Params)
}
}
}
// String returns a JSON pointer
func (n *node) String() string {
if n.parent == nil {
return "/"
}
s := n.path
c := n.parent
for c != nil {
s = c.path + "/" + s
c = c.parent
}
return s
}
// partsToTree transforms a splitted JSON pointer to a tree
// The traversal is iterative to avoid unbounded recursion: depth would otherwise equal
// the number of pointer segments, which an attacker controls through the directive value
func partsToTree(t _type, parts []string, root *node, params *httpsfv.Params) {
n := root
for _, part := range parts {
var child *node
for _, c := range n.children {
if c.path == part {
child = c
break
}
}
if child == nil {
child = &node{path: part, parent: n}
n.children = append(n.children, child)
}
switch t {
case preload:
child.preload = true
child.preloadParams = append(child.preloadParams, params)
case fields:
child.fields = true
child.fieldsParams = append(child.fieldsParams, params)
}
n = child
}
}
// hasChildren checks if the node has at least a child of the given type
func (n *node) hasChildren(t _type) bool {
for _, c := range n.children {
if t == preload && c.preload {
return true
}
if t == fields && c.fields {
return true
}
}
return false
}
// httpList transforms the node in an HTTP Structured Field List
func (n *node) httpList(t _type, prefix string) httpsfv.List {
if len(n.children) == 0 {
if prefix == "" {
return httpsfv.List{}
}
var list httpsfv.List
switch t {
case preload:
for _, params := range n.preloadParams {
list = append(list, httpsfv.Item{Value: prefix, Params: params})
}
case fields:
for _, params := range n.fieldsParams {
list = append(list, httpsfv.Item{Value: prefix, Params: params})
}
}
return list
}
var list httpsfv.List
for _, c := range n.children {
if (t == preload && !c.preload) || (t == fields && !c.fields) {
continue
}
list = append(list, c.httpList(t, prefix+"/"+c.path)...)
}
return list
}