-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathstring.go
More file actions
168 lines (140 loc) · 4.56 KB
/
Copy pathstring.go
File metadata and controls
168 lines (140 loc) · 4.56 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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
package chaff
import (
"fmt"
"strings"
"time"
"github.com/go-faker/faker/v4"
"github.com/ryanolee/go-chaff/internal/regen"
"github.com/ryanolee/go-chaff/internal/util"
)
type (
stringGenerator struct {
Format stringFormat
Pattern string
PatternGenerator regen.Generator
MinLength int
MaxLength int
}
)
type stringFormat string
const (
// Time
formatDateTime stringFormat = "date-time" // RFC3339
formatTime stringFormat = "time" //
formatDate stringFormat = "date"
formatDuration stringFormat = "duration"
// Email
formatEmail stringFormat = "email"
formatIdnEmail stringFormat = "idn-email"
// Hostname
formatHostname stringFormat = "hostname"
formatIdnHostname stringFormat = "idn-hostname"
// IP
formatIpv4 stringFormat = "ipv4"
formatIpv6 stringFormat = "ipv6"
// Rescource Identifier
formatUUID stringFormat = "uuid"
formatURI stringFormat = "uri"
formatURIReference stringFormat = "uri-reference"
formatIRI stringFormat = "iri"
formatIRIReference stringFormat = "iri-reference"
// Uri Template
formatUriTemplate stringFormat = "uri-template"
// JSON Pointer
formatJSONPointer stringFormat = "json-pointer"
formatRelativeJSONPointer stringFormat = "relative-json-pointer"
// Regex
formatRegex stringFormat = "regex"
)
// Parses the "type" keyword of a schema when it is a "string"
// Example:
//
// {
// "type": "string",
// "pattern": "^[a-zA-Z0-9]{3,30}$"
// }
func parseString(node schemaNode, metadata *parserMetadata) (Generator, error) {
if node.Format != nil && node.Pattern != nil {
return nullGenerator{}, fmt.Errorf("cannot have both format and pattern on a string")
}
// Validate length bounds
minLength := util.GetZeroIfNil(node.MinLength, 0)
maxLength := util.GetZeroIfNil(node.MaxLength, 0)
if maxLength < 0 || minLength < 0 {
return nullGenerator{}, fmt.Errorf("min/max length cannot be negative")
}
if minLength > maxLength && maxLength != 0 {
return nullGenerator{}, fmt.Errorf("minLength cannot be greater than maxLength")
}
hasPatternBasedBuilder := node.Pattern != nil || node.Format != nil
hasSetMinMaxLength := node.MinLength != nil || node.MaxLength != nil
if hasPatternBasedBuilder && hasSetMinMaxLength {
return nullGenerator{}, fmt.Errorf("cannot have both pattern/format based builder and min/max length set at the same time")
}
generator := stringGenerator{
Format: stringFormat(util.GetZeroIfNil(node.Format, "")),
Pattern: util.GetZeroIfNil(node.Pattern, ""),
MinLength: minLength,
MaxLength: maxLength,
}
if node.Pattern != nil {
regenGenerator, err := newRegexGenerator(*node.Pattern, metadata.ParserOptions.RegexStringOptions)
if err != nil {
return nullGenerator{}, fmt.Errorf("invalid regex pattern: %s", *node.Pattern)
}
generator.PatternGenerator = regenGenerator
}
return generator, nil
}
func (g stringGenerator) Generate(opts *GeneratorOptions) interface{} {
opts.overallComplexity++
if g.Format != "" {
return generateFormat(g.Format, opts)
}
if g.Pattern != "" {
return g.PatternGenerator.Generate()
}
// Build a string with a single sentence in it
var sb strings.Builder
sb.Write([]byte(faker.Sentence()))
// Keep on filling it until there is a full sentence
for sb.Len() < g.MinLength {
sb.Write([]byte(faker.Sentence()))
}
// Truncate it if it get's too long
if g.MaxLength != 0 && sb.Len() > g.MaxLength {
return sb.String()[:g.MaxLength]
}
return sb.String()
}
func (g stringGenerator) String() string {
return fmt.Sprintf("StringGenerator[%s, %s]", g.Format, g.Pattern)
}
func generateFormat(format stringFormat, opts *GeneratorOptions) string {
switch format {
case formatDateTime:
return time.Unix(faker.UnixTime(), 0).Format(time.RFC3339)
case formatTime:
return fmt.Sprintf("%s+00:00", time.Unix(faker.UnixTime(), 0).Format(time.TimeOnly))
case formatDate:
return time.Unix(faker.UnixTime(), 0).Format(time.DateOnly)
case formatDuration:
return fmt.Sprintf("P%dD", opts.Rand.RandomInt(0, 90))
case formatEmail, formatIdnEmail:
return faker.Email()
case formatHostname, formatIdnHostname:
return faker.DomainName()
case formatIpv4:
return faker.IPv4()
case formatIpv6:
return faker.IPv6()
case formatUUID:
return faker.UUIDHyphenated()
case formatURI, formatURIReference, formatIRI, formatIRIReference:
return faker.URL()
case formatUriTemplate, formatJSONPointer, formatRelativeJSONPointer, formatRegex:
return fmt.Sprintf("Known but unsupported format: %s", format)
default:
return fmt.Sprintf("Unsupported Format: %s", format)
}
}