File: path.go

package info (click to toggle)
golang-github-spiffe-go-spiffe 2.5.0-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 1,116 kB
  • sloc: makefile: 157
file content (107 lines) | stat: -rw-r--r-- 2,541 bytes parent folder | download | duplicates (2)
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
package spiffeid

import (
	"fmt"
	"strings"
)

// FormatPath builds a path by formatting the given formatting string with
// the given args (i.e. fmt.Sprintf). The resulting path must be valid or
// an error is returned.
func FormatPath(format string, args ...interface{}) (string, error) {
	path := fmt.Sprintf(format, args...)
	if err := ValidatePath(path); err != nil {
		return "", err
	}
	return path, nil
}

// JoinPathSegments joins one or more path segments into a slash separated
// path. Segments cannot contain slashes. The resulting path must be valid or
// an error is returned. If no segments are provided, an empty string is
// returned.
func JoinPathSegments(segments ...string) (string, error) {
	var builder strings.Builder
	for _, segment := range segments {
		if err := ValidatePathSegment(segment); err != nil {
			return "", err
		}
		builder.WriteByte('/')
		builder.WriteString(segment)
	}
	return builder.String(), nil
}

// ValidatePath validates that a path string is a conformant path for a SPIFFE
// ID.
// See https://github.com/spiffe/spiffe/blob/main/standards/SPIFFE-ID.md#22-path
func ValidatePath(path string) error {
	switch {
	case path == "":
		return nil
	case path[0] != '/':
		return errNoLeadingSlash
	}

	segmentStart := 0
	segmentEnd := 0
	for ; segmentEnd < len(path); segmentEnd++ {
		c := path[segmentEnd]
		if c == '/' {
			switch path[segmentStart:segmentEnd] {
			case "/":
				return errEmptySegment
			case "/.", "/..":
				return errDotSegment
			}
			segmentStart = segmentEnd
			continue
		}
		if !isValidPathSegmentChar(c) {
			return errBadPathSegmentChar
		}
	}

	switch path[segmentStart:segmentEnd] {
	case "/":
		return errTrailingSlash
	case "/.", "/..":
		return errDotSegment
	}
	return nil
}

// ValidatePathSegment validates that a string is a conformant segment for
// inclusion in the path for a SPIFFE ID.
// See https://github.com/spiffe/spiffe/blob/main/standards/SPIFFE-ID.md#22-path
func ValidatePathSegment(segment string) error {
	switch segment {
	case "":
		return errEmptySegment
	case ".", "..":
		return errDotSegment
	}
	for i := 0; i < len(segment); i++ {
		if !isValidPathSegmentChar(segment[i]) {
			return errBadPathSegmentChar
		}
	}
	return nil
}

func isValidPathSegmentChar(c uint8) bool {
	switch {
	case c >= 'a' && c <= 'z':
		return true
	case c >= 'A' && c <= 'Z':
		return true
	case c >= '0' && c <= '9':
		return true
	case c == '-', c == '.', c == '_':
		return true
	case isBackcompatPathChar(c):
		return true
	default:
		return false
	}
}