github.com/google/osv-scalibr@v0.4.1/veles/secrets/hashicorpvault/validator.go (about) 1 // Copyright 2025 Google LLC 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package hashicorpvault 16 17 import ( 18 "encoding/json" 19 "fmt" 20 "net/http" 21 "time" 22 23 "github.com/google/osv-scalibr/veles/secrets/common/simplevalidate" 24 ) 25 26 const httpClientTimeout = 10 * time.Second 27 28 // NewTokenValidator creates a new TokenValidator. 29 // 30 // Checks whether the given Token is valid by using the Vault token lookup-self API. 31 // It performs a GET request to /v1/auth/token/lookup-self with the token in the X-Vault-Token header. 32 // Returns ValidationValid for 200 OK, ValidationInvalid for 401/403, ValidationFailed for other errors. 33 func NewTokenValidator(vaultURL string) *simplevalidate.Validator[Token] { 34 return &simplevalidate.Validator[Token]{ 35 Endpoint: vaultURL + "/v1/auth/token/lookup-self", 36 HTTPMethod: http.MethodGet, 37 HTTPHeaders: func(k Token) map[string]string { 38 return map[string]string{ 39 "X-Vault-Token": k.Token, 40 } 41 }, 42 ValidResponseCodes: []int{http.StatusOK}, 43 InvalidResponseCodes: []int{http.StatusUnauthorized, http.StatusForbidden}, 44 HTTPC: &http.Client{Timeout: httpClientTimeout}, 45 } 46 } 47 48 // NewAppRoleValidator creates a new AppRoleValidator. 49 // 50 // Validate checks whether the given AppRoleCredentials are valid by using the Vault AppRole login API. 51 // It performs a POST request to /v1/auth/approle/login with role-id and secret-id. 52 // Note: Since the detector cannot distinguish between role-id and secret-id, this validation 53 // is limited. In practice, both values would need to be provided together. 54 // Returns ValidationValid for 200 OK, ValidationInvalid for 401/400, ValidationFailed for other errors. 55 func NewAppRoleValidator(vaultURL string) *simplevalidate.Validator[AppRoleCredentials] { 56 return &simplevalidate.Validator[AppRoleCredentials]{ 57 Endpoint: vaultURL + "/v1/auth/approle/login", 58 HTTPMethod: http.MethodPost, 59 Body: appRoleBody, 60 HTTPHeaders: func(_ AppRoleCredentials) map[string]string { 61 return map[string]string{ 62 "Content-Type": "application/json", 63 } 64 }, 65 ValidResponseCodes: []int{http.StatusOK}, 66 InvalidResponseCodes: []int{http.StatusUnauthorized, http.StatusBadRequest}, 67 HTTPC: &http.Client{Timeout: httpClientTimeout}, 68 } 69 } 70 71 func appRoleBody(creds AppRoleCredentials) (string, error) { 72 if creds.RoleID == "" || creds.SecretID == "" { 73 return "", fmt.Errorf("both role_id and secret_id are required for AppRole validation. Actual creds %v", creds) 74 } 75 body := AppRoleLoginRequest{ 76 RoleID: creds.RoleID, 77 SecretID: creds.SecretID, 78 } 79 jsonBody, err := json.Marshal(body) 80 if err != nil { 81 return "", fmt.Errorf("failed to marshal AppRoleLoginRequest: %w", err) 82 } 83 return string(jsonBody), nil 84 } 85 86 // AppRoleLoginRequest represents the request body for AppRole login. 87 type AppRoleLoginRequest struct { 88 RoleID string `json:"role_id"` 89 SecretID string `json:"secret_id"` 90 }