mirror of
https://github.com/hashicorp/vault.git
synced 2025-08-14 18:47:01 +02:00
* Adding explicit MPL license for sub-package. This directory and its subdirectories (packages) contain files licensed with the MPLv2 `LICENSE` file in this directory and are intentionally licensed separately from the BSL `LICENSE` file at the root of this repository. * Adding explicit MPL license for sub-package. This directory and its subdirectories (packages) contain files licensed with the MPLv2 `LICENSE` file in this directory and are intentionally licensed separately from the BSL `LICENSE` file at the root of this repository. * Updating the license from MPL to Business Source License. Going forward, this project will be licensed under the Business Source License v1.1. Please see our blog post for more details at https://hashi.co/bsl-blog, FAQ at www.hashicorp.com/licensing-faq, and details of the license at www.hashicorp.com/bsl. * add missing license headers * Update copyright file headers to BUS-1.1 * Fix test that expected exact offset on hcl file --------- Co-authored-by: hashicorp-copywrite[bot] <110428419+hashicorp-copywrite[bot]@users.noreply.github.com> Co-authored-by: Sarah Thompson <sthompson@hashicorp.com> Co-authored-by: Brian Kassouf <bkassouf@hashicorp.com>
591 lines
12 KiB
Go
591 lines
12 KiB
Go
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: BUSL-1.1
|
|
|
|
package random
|
|
|
|
import (
|
|
"encoding/json"
|
|
"reflect"
|
|
"testing"
|
|
)
|
|
|
|
func TestParsePolicy(t *testing.T) {
|
|
type testCase struct {
|
|
rawConfig string
|
|
expected StringGenerator
|
|
expectErr bool
|
|
}
|
|
|
|
tests := map[string]testCase{
|
|
"unrecognized rule": {
|
|
rawConfig: `
|
|
length = 20
|
|
rule "testrule" {
|
|
string = "teststring"
|
|
int = 123
|
|
}`,
|
|
expected: StringGenerator{},
|
|
expectErr: true,
|
|
},
|
|
|
|
"charset restrictions": {
|
|
rawConfig: `
|
|
length = 20
|
|
rule "charset" {
|
|
charset = "abcde"
|
|
min-chars = 2
|
|
}`,
|
|
expected: StringGenerator{
|
|
Length: 20,
|
|
charset: []rune("abcde"),
|
|
Rules: []Rule{
|
|
CharsetRule{
|
|
Charset: []rune("abcde"),
|
|
MinChars: 2,
|
|
},
|
|
},
|
|
},
|
|
expectErr: false,
|
|
},
|
|
}
|
|
|
|
for name, test := range tests {
|
|
t.Run(name, func(t *testing.T) {
|
|
actual, err := ParsePolicy(test.rawConfig)
|
|
if test.expectErr && err == nil {
|
|
t.Fatalf("err expected, got nil")
|
|
}
|
|
if !test.expectErr && err != nil {
|
|
t.Fatalf("no error expected, got: %s", err)
|
|
}
|
|
|
|
if !reflect.DeepEqual(actual, test.expected) {
|
|
t.Fatalf("Actual: %#v\nExpected:%#v", actual, test.expected)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestParser_ParsePolicy(t *testing.T) {
|
|
type testCase struct {
|
|
registry map[string]ruleConstructor
|
|
|
|
rawConfig string
|
|
expected StringGenerator
|
|
expectErr bool
|
|
}
|
|
|
|
tests := map[string]testCase{
|
|
"empty config": {
|
|
registry: defaultRuleNameMapping,
|
|
rawConfig: "",
|
|
expected: StringGenerator{},
|
|
expectErr: true,
|
|
},
|
|
"bogus config": {
|
|
registry: defaultRuleNameMapping,
|
|
rawConfig: "asdf",
|
|
expected: StringGenerator{},
|
|
expectErr: true,
|
|
},
|
|
"config with only length": {
|
|
registry: defaultRuleNameMapping,
|
|
rawConfig: `
|
|
length = 20`,
|
|
expected: StringGenerator{},
|
|
expectErr: true,
|
|
},
|
|
"config with zero length": {
|
|
registry: defaultRuleNameMapping,
|
|
rawConfig: `
|
|
length = 0
|
|
rule "charset" {
|
|
charset = "abcde"
|
|
}`,
|
|
expected: StringGenerator{},
|
|
expectErr: true,
|
|
},
|
|
"config with negative length": {
|
|
registry: defaultRuleNameMapping,
|
|
rawConfig: `
|
|
length = -2
|
|
rule "charset" {
|
|
charset = "abcde"
|
|
}`,
|
|
expected: StringGenerator{},
|
|
expectErr: true,
|
|
},
|
|
"charset restrictions": {
|
|
registry: defaultRuleNameMapping,
|
|
rawConfig: `
|
|
length = 20
|
|
rule "charset" {
|
|
charset = "abcde"
|
|
min-chars = 2
|
|
}`,
|
|
expected: StringGenerator{
|
|
Length: 20,
|
|
charset: []rune("abcde"),
|
|
Rules: []Rule{
|
|
CharsetRule{
|
|
Charset: []rune("abcde"),
|
|
MinChars: 2,
|
|
},
|
|
},
|
|
},
|
|
expectErr: false,
|
|
},
|
|
"test rule": {
|
|
registry: map[string]ruleConstructor{
|
|
"testrule": newTestRule,
|
|
},
|
|
rawConfig: `
|
|
length = 20
|
|
rule "testrule" {
|
|
string = "teststring"
|
|
int = 123
|
|
}`,
|
|
expected: StringGenerator{
|
|
Length: 20,
|
|
charset: deduplicateRunes([]rune("teststring")),
|
|
Rules: []Rule{
|
|
testCharsetRule{
|
|
String: "teststring",
|
|
Integer: 123,
|
|
},
|
|
},
|
|
},
|
|
expectErr: false,
|
|
},
|
|
"test rule and charset restrictions": {
|
|
registry: map[string]ruleConstructor{
|
|
"testrule": newTestRule,
|
|
"charset": ParseCharset,
|
|
},
|
|
rawConfig: `
|
|
length = 20
|
|
rule "testrule" {
|
|
string = "teststring"
|
|
int = 123
|
|
}
|
|
rule "charset" {
|
|
charset = "abcde"
|
|
min-chars = 2
|
|
}`,
|
|
expected: StringGenerator{
|
|
Length: 20,
|
|
charset: deduplicateRunes([]rune("abcdeteststring")),
|
|
Rules: []Rule{
|
|
testCharsetRule{
|
|
String: "teststring",
|
|
Integer: 123,
|
|
},
|
|
CharsetRule{
|
|
Charset: []rune("abcde"),
|
|
MinChars: 2,
|
|
},
|
|
},
|
|
},
|
|
expectErr: false,
|
|
},
|
|
"unrecognized rule": {
|
|
registry: defaultRuleNameMapping,
|
|
rawConfig: `
|
|
length = 20
|
|
rule "testrule" {
|
|
string = "teststring"
|
|
int = 123
|
|
}`,
|
|
expected: StringGenerator{},
|
|
expectErr: true,
|
|
},
|
|
|
|
// /////////////////////////////////////////////////
|
|
// JSON data
|
|
"manually JSONified HCL": {
|
|
registry: map[string]ruleConstructor{
|
|
"testrule": newTestRule,
|
|
"charset": ParseCharset,
|
|
},
|
|
rawConfig: `
|
|
{
|
|
"charset": "abcde",
|
|
"length": 20,
|
|
"rule": [
|
|
{
|
|
"testrule": [
|
|
{
|
|
"string": "teststring",
|
|
"int": 123
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"charset": [
|
|
{
|
|
"charset": "abcde",
|
|
"min-chars": 2
|
|
}
|
|
]
|
|
}
|
|
]
|
|
}`,
|
|
expected: StringGenerator{
|
|
Length: 20,
|
|
charset: deduplicateRunes([]rune("abcdeteststring")),
|
|
Rules: []Rule{
|
|
testCharsetRule{
|
|
String: "teststring",
|
|
Integer: 123,
|
|
},
|
|
CharsetRule{
|
|
Charset: []rune("abcde"),
|
|
MinChars: 2,
|
|
},
|
|
},
|
|
},
|
|
expectErr: false,
|
|
},
|
|
"JSONified HCL": {
|
|
registry: map[string]ruleConstructor{
|
|
"testrule": newTestRule,
|
|
"charset": ParseCharset,
|
|
},
|
|
rawConfig: toJSON(t, StringGenerator{
|
|
Length: 20,
|
|
Rules: []Rule{
|
|
testCharsetRule{
|
|
String: "teststring",
|
|
Integer: 123,
|
|
},
|
|
CharsetRule{
|
|
Charset: []rune("abcde"),
|
|
MinChars: 2,
|
|
},
|
|
},
|
|
}),
|
|
expected: StringGenerator{
|
|
Length: 20,
|
|
charset: deduplicateRunes([]rune("abcdeteststring")),
|
|
Rules: []Rule{
|
|
testCharsetRule{
|
|
String: "teststring",
|
|
Integer: 123,
|
|
},
|
|
CharsetRule{
|
|
Charset: []rune("abcde"),
|
|
MinChars: 2,
|
|
},
|
|
},
|
|
},
|
|
expectErr: false,
|
|
},
|
|
"JSON unrecognized rule": {
|
|
registry: defaultRuleNameMapping,
|
|
rawConfig: `
|
|
{
|
|
"charset": "abcde",
|
|
"length": 20,
|
|
"rule": [
|
|
{
|
|
"testrule": [
|
|
{
|
|
"string": "teststring",
|
|
"int": 123
|
|
}
|
|
],
|
|
}
|
|
]
|
|
}`,
|
|
expected: StringGenerator{},
|
|
expectErr: true,
|
|
},
|
|
"config value with empty slice": {
|
|
registry: defaultRuleNameMapping,
|
|
rawConfig: `
|
|
rule {
|
|
n = []
|
|
}`,
|
|
expected: StringGenerator{},
|
|
expectErr: true,
|
|
},
|
|
}
|
|
|
|
for name, test := range tests {
|
|
t.Run(name, func(t *testing.T) {
|
|
parser := PolicyParser{
|
|
RuleRegistry: Registry{
|
|
Rules: test.registry,
|
|
},
|
|
}
|
|
|
|
actual, err := parser.ParsePolicy(test.rawConfig)
|
|
if test.expectErr && err == nil {
|
|
t.Fatalf("err expected, got nil")
|
|
}
|
|
if !test.expectErr && err != nil {
|
|
t.Fatalf("no error expected, got: %s", err)
|
|
}
|
|
|
|
if !reflect.DeepEqual(actual, test.expected) {
|
|
t.Fatalf("Actual: %#v\nExpected:%#v", actual, test.expected)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestParseRules(t *testing.T) {
|
|
type testCase struct {
|
|
registry map[string]ruleConstructor
|
|
|
|
rawRules []map[string]interface{}
|
|
expectedRules []Rule
|
|
expectErr bool
|
|
}
|
|
|
|
tests := map[string]testCase{
|
|
"nil rule data": {
|
|
registry: defaultRuleNameMapping,
|
|
rawRules: nil,
|
|
expectedRules: nil,
|
|
expectErr: false,
|
|
},
|
|
"empty rule data": {
|
|
registry: defaultRuleNameMapping,
|
|
rawRules: []map[string]interface{}{},
|
|
expectedRules: nil,
|
|
expectErr: false,
|
|
},
|
|
"invalid rule data": {
|
|
registry: defaultRuleNameMapping,
|
|
rawRules: []map[string]interface{}{
|
|
{
|
|
"testrule": map[string]interface{}{
|
|
"string": "teststring",
|
|
},
|
|
},
|
|
},
|
|
expectedRules: nil,
|
|
expectErr: true,
|
|
},
|
|
"unrecognized rule data": {
|
|
registry: defaultRuleNameMapping,
|
|
rawRules: []map[string]interface{}{
|
|
{
|
|
"testrule": []map[string]interface{}{
|
|
{
|
|
"string": "teststring",
|
|
"int": 123,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
expectedRules: nil,
|
|
expectErr: true,
|
|
},
|
|
"recognized rule": {
|
|
registry: map[string]ruleConstructor{
|
|
"testrule": newTestRule,
|
|
},
|
|
rawRules: []map[string]interface{}{
|
|
{
|
|
"testrule": []map[string]interface{}{
|
|
{
|
|
"string": "teststring",
|
|
"int": 123,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
expectedRules: []Rule{
|
|
testCharsetRule{
|
|
String: "teststring",
|
|
Integer: 123,
|
|
},
|
|
},
|
|
expectErr: false,
|
|
},
|
|
}
|
|
|
|
for name, test := range tests {
|
|
t.Run(name, func(t *testing.T) {
|
|
registry := Registry{
|
|
Rules: test.registry,
|
|
}
|
|
|
|
actualRules, err := parseRules(registry, test.rawRules)
|
|
if test.expectErr && err == nil {
|
|
t.Fatalf("err expected, got nil")
|
|
}
|
|
if !test.expectErr && err != nil {
|
|
t.Fatalf("no error expected, got: %s", err)
|
|
}
|
|
|
|
if !reflect.DeepEqual(actualRules, test.expectedRules) {
|
|
t.Fatalf("Actual: %#v\nExpected:%#v", actualRules, test.expectedRules)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGetMapSlice(t *testing.T) {
|
|
type testCase struct {
|
|
input map[string]interface{}
|
|
key string
|
|
expectedSlice []map[string]interface{}
|
|
expectErr bool
|
|
}
|
|
|
|
tests := map[string]testCase{
|
|
"nil map": {
|
|
input: nil,
|
|
key: "testkey",
|
|
expectedSlice: nil,
|
|
expectErr: false,
|
|
},
|
|
"empty map": {
|
|
input: map[string]interface{}{},
|
|
key: "testkey",
|
|
expectedSlice: nil,
|
|
expectErr: false,
|
|
},
|
|
"ignored keys": {
|
|
input: map[string]interface{}{
|
|
"foo": "bar",
|
|
},
|
|
key: "testkey",
|
|
expectedSlice: nil,
|
|
expectErr: false,
|
|
},
|
|
"key has wrong type": {
|
|
input: map[string]interface{}{
|
|
"foo": "bar",
|
|
},
|
|
key: "foo",
|
|
expectedSlice: nil,
|
|
expectErr: true,
|
|
},
|
|
"good data": {
|
|
input: map[string]interface{}{
|
|
"foo": []map[string]interface{}{
|
|
{
|
|
"sub-foo": "bar",
|
|
},
|
|
},
|
|
},
|
|
key: "foo",
|
|
expectedSlice: []map[string]interface{}{
|
|
{
|
|
"sub-foo": "bar",
|
|
},
|
|
},
|
|
expectErr: false,
|
|
},
|
|
}
|
|
|
|
for name, test := range tests {
|
|
t.Run(name, func(t *testing.T) {
|
|
actualSlice, err := getMapSlice(test.input, test.key)
|
|
if test.expectErr && err == nil {
|
|
t.Fatalf("err expected, got nil")
|
|
}
|
|
if !test.expectErr && err != nil {
|
|
t.Fatalf("no error expected, got: %s", err)
|
|
}
|
|
|
|
if !reflect.DeepEqual(actualSlice, test.expectedSlice) {
|
|
t.Fatalf("Actual: %#v\nExpected:%#v", actualSlice, test.expectedSlice)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGetRuleInfo(t *testing.T) {
|
|
type testCase struct {
|
|
rule map[string]interface{}
|
|
expectedInfo ruleInfo
|
|
expectErr bool
|
|
}
|
|
|
|
tests := map[string]testCase{
|
|
"nil rule": {
|
|
rule: nil,
|
|
expectedInfo: ruleInfo{},
|
|
expectErr: true,
|
|
},
|
|
"empty rule": {
|
|
rule: map[string]interface{}{},
|
|
expectedInfo: ruleInfo{},
|
|
expectErr: true,
|
|
},
|
|
"rule with invalid type": {
|
|
rule: map[string]interface{}{
|
|
"TestRuleType": "wrong type",
|
|
},
|
|
expectedInfo: ruleInfo{},
|
|
expectErr: true,
|
|
},
|
|
"rule with good data": {
|
|
rule: map[string]interface{}{
|
|
"TestRuleType": []map[string]interface{}{
|
|
{
|
|
"foo": "bar",
|
|
},
|
|
},
|
|
},
|
|
expectedInfo: ruleInfo{
|
|
ruleType: "TestRuleType",
|
|
data: map[string]interface{}{
|
|
"foo": "bar",
|
|
},
|
|
},
|
|
expectErr: false,
|
|
},
|
|
}
|
|
|
|
for name, test := range tests {
|
|
t.Run(name, func(t *testing.T) {
|
|
actualInfo, err := getRuleInfo(test.rule)
|
|
if test.expectErr && err == nil {
|
|
t.Fatalf("err expected, got nil")
|
|
}
|
|
if !test.expectErr && err != nil {
|
|
t.Fatalf("no error expected, got: %s", err)
|
|
}
|
|
|
|
if !reflect.DeepEqual(actualInfo, test.expectedInfo) {
|
|
t.Fatalf("Actual: %#v\nExpected:%#v", actualInfo, test.expectedInfo)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func BenchmarkParser_Parse(b *testing.B) {
|
|
config := `length = 20
|
|
rule "charset" {
|
|
charset = "abcde"
|
|
min-chars = 2
|
|
}`
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
parser := PolicyParser{
|
|
RuleRegistry: Registry{
|
|
Rules: defaultRuleNameMapping,
|
|
},
|
|
}
|
|
_, err := parser.ParsePolicy(config)
|
|
if err != nil {
|
|
b.Fatalf("Failed to parse: %s", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func toJSON(t *testing.T, val interface{}) string {
|
|
t.Helper()
|
|
b, err := json.Marshal(val)
|
|
if err != nil {
|
|
t.Fatalf("unable to marshal to JSON: %s", err)
|
|
}
|
|
return string(b)
|
|
}
|