mirror of
				https://github.com/optim-enterprises-bv/vault.git
				synced 2025-11-04 04:28:08 +00: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)
 | 
						|
}
 |