mirror of
				https://github.com/optim-enterprises-bv/vault.git
				synced 2025-10-31 02:28:09 +00:00 
			
		
		
		
	Add new /sys/well-known interface to get information about registered labels (#25695)
* Add new /sys/well-known interface to get information about registered labels - Add two new interfaces LIST/GET /sys/well-known which will provide a list of keys which are registered labels within the /.well-known space on the local server, along with a detailed info map for each - Add GET /sys/well-known/<label> to get details on a specific registered label - Add docs and tests for the new api endpoints * Add test doc and remove copied comment * Rename returned fields to use snake case * Remove extra newline added when resolving the merge conflict * Apply suggestions from code review Co-authored-by: Sarah Chavis <62406755+schavis@users.noreply.github.com> --------- Co-authored-by: Sarah Chavis <62406755+schavis@users.noreply.github.com>
This commit is contained in:
		| @@ -15,6 +15,7 @@ import ( | |||||||
| 	"hash" | 	"hash" | ||||||
| 	"math/rand" | 	"math/rand" | ||||||
| 	"net/http" | 	"net/http" | ||||||
|  | 	"net/url" | ||||||
| 	"path" | 	"path" | ||||||
| 	"path/filepath" | 	"path/filepath" | ||||||
| 	"runtime" | 	"runtime" | ||||||
| @@ -224,6 +225,7 @@ func NewSystemBackend(core *Core, logger log.Logger, config *logical.BackendConf | |||||||
| 	b.Backend.Paths = append(b.Backend.Paths, b.loginMFAPaths()...) | 	b.Backend.Paths = append(b.Backend.Paths, b.loginMFAPaths()...) | ||||||
| 	b.Backend.Paths = append(b.Backend.Paths, b.experimentPaths()...) | 	b.Backend.Paths = append(b.Backend.Paths, b.experimentPaths()...) | ||||||
| 	b.Backend.Paths = append(b.Backend.Paths, b.introspectionPaths()...) | 	b.Backend.Paths = append(b.Backend.Paths, b.introspectionPaths()...) | ||||||
|  | 	b.Backend.Paths = append(b.Backend.Paths, b.wellKnownPaths()...) | ||||||
|  |  | ||||||
| 	if core.rawEnabled { | 	if core.rawEnabled { | ||||||
| 		b.Backend.Paths = append(b.Backend.Paths, b.rawPaths()...) | 		b.Backend.Paths = append(b.Backend.Paths, b.rawPaths()...) | ||||||
| @@ -6001,6 +6003,62 @@ func (b *SystemBackend) handleReadExperiments(ctx context.Context, _ *logical.Re | |||||||
| 	}, nil | 	}, nil | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // handleWellKnownList handles /sys/well-known/ endpoint to provide the list of registered labels | ||||||
|  | // within the /.well-known path on this server | ||||||
|  | func (b *SystemBackend) handleWellKnownList() framework.OperationFunc { | ||||||
|  | 	return func(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) { | ||||||
|  | 		labels := b.Core.WellKnownRedirects.List() | ||||||
|  |  | ||||||
|  | 		respKeys := []string{} | ||||||
|  | 		respKeyInfo := map[string]interface{}{} | ||||||
|  |  | ||||||
|  | 		for label, wk := range labels { | ||||||
|  | 			respKeys = append(respKeys, label) | ||||||
|  |  | ||||||
|  | 			mountPath := "" | ||||||
|  | 			m := b.Core.router.MatchingMountByUUID(wk.mountUUID) | ||||||
|  | 			if m != nil { | ||||||
|  | 				mountPath, _ = url.JoinPath(m.namespace.Path, m.Path) | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			respKeyInfo[label] = map[string]interface{}{ | ||||||
|  | 				"mount_path": mountPath, | ||||||
|  | 				"mount_uuid": wk.mountUUID, | ||||||
|  | 				"prefix":     wk.prefix, | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		return logical.ListResponseWithInfo(respKeys, respKeyInfo), nil | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // handleWellKnownRead handles the "/sys/well-known/<name>" endpoint to read a registered well-known label | ||||||
|  | func (b *SystemBackend) handleWellKnownRead() framework.OperationFunc { | ||||||
|  | 	return func(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) { | ||||||
|  | 		label := data.Get("label").(string) | ||||||
|  |  | ||||||
|  | 		wk, ok := b.Core.WellKnownRedirects.Get(label) | ||||||
|  | 		if !ok { | ||||||
|  | 			return nil, nil | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		mountPath := "" | ||||||
|  | 		m := b.Core.router.MatchingMountByUUID(wk.mountUUID) | ||||||
|  | 		if m != nil { | ||||||
|  | 			mountPath, _ = url.JoinPath(m.namespace.Path, m.Path) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		return &logical.Response{ | ||||||
|  | 			Data: map[string]interface{}{ | ||||||
|  | 				"label":      label, | ||||||
|  | 				"mount_path": mountPath, | ||||||
|  | 				"mount_uuid": wk.mountUUID, | ||||||
|  | 				"prefix":     wk.prefix, | ||||||
|  | 			}, | ||||||
|  | 		}, nil | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
| func sanitizePath(path string) string { | func sanitizePath(path string) string { | ||||||
| 	if !strings.HasSuffix(path, "/") { | 	if !strings.HasSuffix(path, "/") { | ||||||
| 		path += "/" | 		path += "/" | ||||||
| @@ -7005,4 +7063,31 @@ This path responds to the following HTTP methods. | |||||||
| 		returns the manual license reporting data. | 		returns the manual license reporting data. | ||||||
| 		`, | 		`, | ||||||
| 	}, | 	}, | ||||||
|  |  | ||||||
|  | 	"well-known-list": { | ||||||
|  | 		`List the registered well-known labels to associated mounts.`, | ||||||
|  | 		` | ||||||
|  | This path responds to the following HTTP methods. | ||||||
|  |     LIST / | ||||||
|  |         List the names of the registered labels within the .well-known folder on this server. | ||||||
|  |  | ||||||
|  |     GET / | ||||||
|  |         List the names of the registered labels within the .well-known folder on this server. | ||||||
|  |  | ||||||
|  |     GET /<label> | ||||||
|  |         Retrieve the information regarding a specific registered label on this server. | ||||||
|  | 	    `, | ||||||
|  | 	}, | ||||||
|  |  | ||||||
|  | 	"well-known": { | ||||||
|  | 		`Read the associated mount info for a registered label within the well-known path prefix`, | ||||||
|  | 		` | ||||||
|  |         Retrieve the information regarding a specific registered label on this server. | ||||||
|  | 		`, | ||||||
|  | 	}, | ||||||
|  |  | ||||||
|  | 	"well-known-label": { | ||||||
|  | 		`The label representing a path-prefix within the /.well-known/ path`, | ||||||
|  | 		"", | ||||||
|  | 	}, | ||||||
| } | } | ||||||
|   | |||||||
| @@ -5200,3 +5200,98 @@ func (b *SystemBackend) eventPaths() []*framework.Path { | |||||||
| 		}, | 		}, | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func (b *SystemBackend) wellKnownPaths() []*framework.Path { | ||||||
|  | 	return []*framework.Path{ | ||||||
|  | 		{ | ||||||
|  | 			Pattern: "well-known/?$", | ||||||
|  |  | ||||||
|  | 			DisplayAttrs: &framework.DisplayAttributes{ | ||||||
|  | 				OperationPrefix: "well-known", | ||||||
|  | 				OperationVerb:   "list", | ||||||
|  | 			}, | ||||||
|  |  | ||||||
|  | 			Operations: map[logical.Operation]framework.OperationHandler{ | ||||||
|  | 				logical.ReadOperation: &framework.PathOperation{ | ||||||
|  | 					Callback: b.handleWellKnownList(), | ||||||
|  | 					Responses: map[int][]framework.Response{ | ||||||
|  | 						http.StatusOK: {{ | ||||||
|  | 							Description: "OK", | ||||||
|  | 							Fields: map[string]*framework.FieldSchema{ | ||||||
|  | 								"keys": { | ||||||
|  | 									Type:     framework.TypeStringSlice, | ||||||
|  | 									Required: true, | ||||||
|  | 								}, | ||||||
|  | 							}, | ||||||
|  | 						}}, | ||||||
|  | 					}, | ||||||
|  | 				}, | ||||||
|  | 				logical.ListOperation: &framework.PathOperation{ | ||||||
|  | 					Callback: b.handleWellKnownList(), | ||||||
|  | 					Responses: map[int][]framework.Response{ | ||||||
|  | 						http.StatusOK: {{ | ||||||
|  | 							Description: "OK", | ||||||
|  | 							Fields: map[string]*framework.FieldSchema{ | ||||||
|  | 								"keys": { | ||||||
|  | 									Type:     framework.TypeStringSlice, | ||||||
|  | 									Required: true, | ||||||
|  | 								}, | ||||||
|  | 							}, | ||||||
|  | 						}}, | ||||||
|  | 					}, | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  |  | ||||||
|  | 			HelpSynopsis:    strings.TrimSpace(sysHelp["well-known-list"][0]), | ||||||
|  | 			HelpDescription: strings.TrimSpace(sysHelp["well-known-list"][1]), | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			Pattern: "well-known/(?P<label>.+)", | ||||||
|  |  | ||||||
|  | 			DisplayAttrs: &framework.DisplayAttributes{ | ||||||
|  | 				OperationPrefix: "well-known", | ||||||
|  | 				OperationSuffix: "label", | ||||||
|  | 			}, | ||||||
|  |  | ||||||
|  | 			Fields: map[string]*framework.FieldSchema{ | ||||||
|  | 				"label": { | ||||||
|  | 					Type:        framework.TypeString, | ||||||
|  | 					Description: strings.TrimSpace(sysHelp["well-known-label"][0]), | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  |  | ||||||
|  | 			Operations: map[logical.Operation]framework.OperationHandler{ | ||||||
|  | 				logical.ReadOperation: &framework.PathOperation{ | ||||||
|  | 					Callback: b.handleWellKnownRead(), | ||||||
|  | 					Responses: map[int][]framework.Response{ | ||||||
|  | 						http.StatusOK: {{ | ||||||
|  | 							Description: "OK", | ||||||
|  | 							Fields: map[string]*framework.FieldSchema{ | ||||||
|  | 								"label": { | ||||||
|  | 									Type:     framework.TypeString, | ||||||
|  | 									Required: true, | ||||||
|  | 								}, | ||||||
|  | 								"mount_uuid": { | ||||||
|  | 									Type:     framework.TypeString, | ||||||
|  | 									Required: true, | ||||||
|  | 								}, | ||||||
|  | 								"mount_path": { | ||||||
|  | 									Type:     framework.TypeString, | ||||||
|  | 									Required: true, | ||||||
|  | 								}, | ||||||
|  | 								"prefix": { | ||||||
|  | 									Type:     framework.TypeString, | ||||||
|  | 									Required: true, | ||||||
|  | 								}, | ||||||
|  | 							}, | ||||||
|  | 						}}, | ||||||
|  | 					}, | ||||||
|  | 					Summary: "Retrieve the associated mount information for a registered well-known label.", | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  |  | ||||||
|  | 			HelpSynopsis:    strings.TrimSpace(sysHelp["well-known"][0]), | ||||||
|  | 			HelpDescription: strings.TrimSpace(sysHelp["well-known"][1]), | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|   | |||||||
| @@ -24,6 +24,7 @@ import ( | |||||||
| 	"github.com/hashicorp/go-hclog" | 	"github.com/hashicorp/go-hclog" | ||||||
| 	wrapping "github.com/hashicorp/go-kms-wrapping/v2" | 	wrapping "github.com/hashicorp/go-kms-wrapping/v2" | ||||||
| 	aeadwrapper "github.com/hashicorp/go-kms-wrapping/wrappers/aead/v2" | 	aeadwrapper "github.com/hashicorp/go-kms-wrapping/wrappers/aead/v2" | ||||||
|  | 	"github.com/hashicorp/go-uuid" | ||||||
| 	credUserpass "github.com/hashicorp/vault/builtin/credential/userpass" | 	credUserpass "github.com/hashicorp/vault/builtin/credential/userpass" | ||||||
| 	"github.com/hashicorp/vault/helper/builtinplugins" | 	"github.com/hashicorp/vault/helper/builtinplugins" | ||||||
| 	"github.com/hashicorp/vault/helper/experiments" | 	"github.com/hashicorp/vault/helper/experiments" | ||||||
| @@ -7023,3 +7024,52 @@ func TestGetSealStatus_RedactionSettings(t *testing.T) { | |||||||
| 	assert.Empty(t, resp.BuildDate) | 	assert.Empty(t, resp.BuildDate) | ||||||
| 	assert.Empty(t, resp.ClusterName) | 	assert.Empty(t, resp.ClusterName) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // TestWellKnownSysApi verifies the GET/LIST endpoints of /sys/well-known and /sys/well-known/<label> | ||||||
|  | func TestWellKnownSysApi(t *testing.T) { | ||||||
|  | 	c, _, _ := TestCoreUnsealed(t) | ||||||
|  | 	b := c.systemBackend | ||||||
|  |  | ||||||
|  | 	myUuid, err := uuid.GenerateUUID() | ||||||
|  | 	require.NoError(t, err, "failed generating uuid") | ||||||
|  |  | ||||||
|  | 	err = c.WellKnownRedirects.TryRegister(context.Background(), c, myUuid, "mylabel1", "test-path/foo1") | ||||||
|  | 	require.NoError(t, err, "failed registering well-known redirect 1") | ||||||
|  |  | ||||||
|  | 	err = c.WellKnownRedirects.TryRegister(context.Background(), c, myUuid, "mylabel2", "test-path/foo2") | ||||||
|  | 	require.NoError(t, err, "failed registering well-known redirect 2") | ||||||
|  |  | ||||||
|  | 	// Test LIST operation | ||||||
|  | 	req := logical.TestRequest(t, logical.ListOperation, "well-known") | ||||||
|  | 	resp, err := b.HandleRequest(namespace.RootContext(nil), req) | ||||||
|  | 	require.NoError(t, err, "failed list well-known request") | ||||||
|  | 	require.NotNil(t, resp, "response from list well-known request was nil") | ||||||
|  |  | ||||||
|  | 	require.Contains(t, resp.Data["keys"], "mylabel1") | ||||||
|  | 	require.Contains(t, resp.Data["keys"], "mylabel2") | ||||||
|  | 	require.Len(t, resp.Data["keys"], 2) | ||||||
|  |  | ||||||
|  | 	keyInfo := resp.Data["key_info"].(map[string]interface{}) | ||||||
|  | 	keyInfoLabel1 := keyInfo["mylabel1"].(map[string]interface{}) | ||||||
|  | 	require.Equal(t, myUuid, keyInfoLabel1["mount_uuid"]) | ||||||
|  | 	require.Equal(t, "test-path/foo1", keyInfoLabel1["prefix"]) | ||||||
|  |  | ||||||
|  | 	keyInfoLabel2 := keyInfo["mylabel2"].(map[string]interface{}) | ||||||
|  | 	require.Equal(t, myUuid, keyInfoLabel2["mount_uuid"]) | ||||||
|  | 	require.Equal(t, "test-path/foo2", keyInfoLabel2["prefix"]) | ||||||
|  |  | ||||||
|  | 	// Test GET operation | ||||||
|  | 	req = logical.TestRequest(t, logical.ReadOperation, "well-known/mylabel1") | ||||||
|  | 	resp, err = b.HandleRequest(namespace.RootContext(nil), req) | ||||||
|  | 	require.NoError(t, err, "failed get well-known request") | ||||||
|  | 	require.NotNil(t, resp, "response from get well-known request was nil") | ||||||
|  |  | ||||||
|  | 	require.Equal(t, myUuid, resp.Data["mount_uuid"]) | ||||||
|  | 	require.Equal(t, "test-path/foo1", resp.Data["prefix"]) | ||||||
|  |  | ||||||
|  | 	// Test GET operation on unknown label | ||||||
|  | 	req = logical.TestRequest(t, logical.ReadOperation, "well-known/mylabel1-unknown") | ||||||
|  | 	resp, err = b.HandleRequest(namespace.RootContext(nil), req) | ||||||
|  | 	require.NoError(t, err, "failed get well-known request") | ||||||
|  | 	require.Nil(t, resp, "response from unknown should have been nil was %v", resp) | ||||||
|  | } | ||||||
|   | |||||||
| @@ -18,11 +18,10 @@ type wellKnownRedirect struct { | |||||||
| 	c         *Core | 	c         *Core | ||||||
| 	mountUUID string | 	mountUUID string | ||||||
| 	prefix    string | 	prefix    string | ||||||
| 	isPrefixMatch bool |  | ||||||
| } | } | ||||||
|  |  | ||||||
| type wellKnownRedirectRegistry struct { | type wellKnownRedirectRegistry struct { | ||||||
| 	lock  sync.Mutex | 	lock  sync.RWMutex | ||||||
| 	paths *radix.Tree | 	paths *radix.Tree | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -54,6 +53,9 @@ func (reg *wellKnownRedirectRegistry) TryRegister(ctx context.Context, core *Cor | |||||||
|  |  | ||||||
| // Find any relevant redirects for a given source path | // Find any relevant redirects for a given source path | ||||||
| func (reg *wellKnownRedirectRegistry) Find(path string) (*wellKnownRedirect, string) { | func (reg *wellKnownRedirectRegistry) Find(path string) (*wellKnownRedirect, string) { | ||||||
|  | 	reg.lock.RLock() | ||||||
|  | 	defer reg.lock.RUnlock() | ||||||
|  |  | ||||||
| 	s, a, found := reg.paths.LongestPrefix(path) | 	s, a, found := reg.paths.LongestPrefix(path) | ||||||
| 	if found { | 	if found { | ||||||
| 		remaining := strings.TrimPrefix(path, s) | 		remaining := strings.TrimPrefix(path, s) | ||||||
| @@ -107,6 +109,32 @@ func (reg *wellKnownRedirectRegistry) DeregisterSource(mountUuid, src string) bo | |||||||
| 	return found | 	return found | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // List returns a map keyed by the registered label and associated well known redirect | ||||||
|  | func (reg *wellKnownRedirectRegistry) List() map[string]*wellKnownRedirect { | ||||||
|  | 	reg.lock.RLock() | ||||||
|  | 	defer reg.lock.RUnlock() | ||||||
|  |  | ||||||
|  | 	labels := map[string]*wellKnownRedirect{} | ||||||
|  | 	reg.paths.Walk(func(s string, v interface{}) bool { | ||||||
|  | 		labels[s] = v.(*wellKnownRedirect) | ||||||
|  | 		return false | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	return labels | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Get returns a well known redirect for a specific registered label | ||||||
|  | func (reg *wellKnownRedirectRegistry) Get(label string) (*wellKnownRedirect, bool) { | ||||||
|  | 	reg.lock.RLock() | ||||||
|  | 	defer reg.lock.RUnlock() | ||||||
|  |  | ||||||
|  | 	if v, ok := reg.paths.Get(label); ok { | ||||||
|  | 		return v.(*wellKnownRedirect), true | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return nil, false | ||||||
|  | } | ||||||
|  |  | ||||||
| // Construct the full destination of the redirect, including any remaining path past the src | // Construct the full destination of the redirect, including any remaining path past the src | ||||||
| func (a *wellKnownRedirect) Destination(remaining string) (string, error) { | func (a *wellKnownRedirect) Destination(remaining string) (string, error) { | ||||||
| 	var destPath string | 	var destPath string | ||||||
|   | |||||||
							
								
								
									
										74
									
								
								website/content/api-docs/system/well-known.mdx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								website/content/api-docs/system/well-known.mdx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,74 @@ | |||||||
|  | --- | ||||||
|  | layout: api | ||||||
|  | page_title: /sys/well-known - HTTP API | ||||||
|  | description: The `/sys/well-known` endpoint is used to list and read registered .well-known labels in Vault. | ||||||
|  | --- | ||||||
|  |  | ||||||
|  | # `/sys/well-known` | ||||||
|  |  | ||||||
|  | Use the `/sys/well-known` endpoint to list and read registered labels on the `.well-known/` path prefix for your Vault instance. | ||||||
|  |  | ||||||
|  | ## List well-known | ||||||
|  |  | ||||||
|  | List all registered labels. | ||||||
|  |  | ||||||
|  | | Method | Path              | | ||||||
|  | |:-------|:------------------| | ||||||
|  | | `GET`  | `/sys/well-known` | | ||||||
|  | | `LIST` | `/sys/well-known` | | ||||||
|  |  | ||||||
|  | ### Sample request | ||||||
|  |  | ||||||
|  | ```shell-session | ||||||
|  | $ curl \ | ||||||
|  |     --header "X-Vault-Token: ..." \ | ||||||
|  |     http://127.0.0.1:8200/v1/sys/well-known | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | ### Sample response | ||||||
|  |  | ||||||
|  | ```json | ||||||
|  | { | ||||||
|  |   "key_info": { | ||||||
|  |     "est/cacerts": { | ||||||
|  |       "mount_path": "ns1/pki_int/", | ||||||
|  |       "mount_uuid": "fc9d3ee4-ae92-4e3e-c0e1-a1fdb3e3b8cf", | ||||||
|  |       "prefix": "est/cacerts" | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   "keys": [ | ||||||
|  |     "est/cacerts" | ||||||
|  |   ] | ||||||
|  | } | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | ## Read well-known | ||||||
|  |  | ||||||
|  | Retrieve information for the specified label. | ||||||
|  |  | ||||||
|  | | Method | Path                     | | ||||||
|  | | :----- |:-------------------------| | ||||||
|  | | `GET`  | `/sys/well-known/:label` | | ||||||
|  |  | ||||||
|  | ### Parameters | ||||||
|  |  | ||||||
|  | - `label` `(string: <required>)` – URL path parameter specifying the registered label to fetch. | ||||||
|  |  | ||||||
|  | ### Sample request | ||||||
|  |  | ||||||
|  | ```shell-session | ||||||
|  | $ curl \ | ||||||
|  |     --header "X-Vault-Token: ..." \ | ||||||
|  |     http://127.0.0.1:8200/v1/sys/well-known/est/cacerts | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | ### Sample response | ||||||
|  |  | ||||||
|  | ```json | ||||||
|  | { | ||||||
|  |   "label": "est/cacerts", | ||||||
|  |   "mount_path": "ns1/pki_int/", | ||||||
|  |   "mount_uuid": "fc9d3ee4-ae92-4e3e-c0e1-a1fdb3e3b8cf", | ||||||
|  |   "prefix": "est/cacerts" | ||||||
|  | } | ||||||
|  | ``` | ||||||
| @@ -761,6 +761,10 @@ | |||||||
|         "title": "<code>/sys/version-history</code>", |         "title": "<code>/sys/version-history</code>", | ||||||
|         "path": "system/version-history" |         "path": "system/version-history" | ||||||
|       }, |       }, | ||||||
|  |       { | ||||||
|  |         "title": "<code>/sys/well-known</code>", | ||||||
|  |         "path": "system/well-known" | ||||||
|  |       }, | ||||||
|       { |       { | ||||||
|         "title": "<code>/sys/wrapping/lookup</code>", |         "title": "<code>/sys/wrapping/lookup</code>", | ||||||
|         "path": "system/wrapping-lookup" |         "path": "system/wrapping-lookup" | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Steven Clark
					Steven Clark