github.com/snyk/vervet/v6@v6.2.4/include_headers.go (about) 1 package vervet 2 3 import ( 4 "fmt" 5 "net/url" 6 "path/filepath" 7 8 "github.com/getkin/kin-openapi/openapi3" 9 ) 10 11 const ( 12 // ExtSnykIncludeHeaders is used to annotate a response with a list of 13 // headers. While OpenAPI supports header references, it does not yet 14 // support including a collection of common headers. This extension is used 15 // by vervet to include headers from a referenced document when compiling 16 // OpenAPI specs. 17 ExtSnykIncludeHeaders = "x-snyk-include-headers" 18 ) 19 20 // IncludeHeaders adds response headers included with the ExtSnykIncludeHeaders 21 // extension property. 22 func IncludeHeaders(doc *Document) error { 23 w := &includeHeaders{doc: doc} 24 if err := w.apply(); err != nil { 25 return err 26 } 27 return doc.ResolveRefs() 28 } 29 30 type includeHeaders struct { 31 doc *Document 32 } 33 34 func (w *includeHeaders) apply() error { 35 for _, pathItem := range w.doc.Paths { 36 if err := w.applyOperation(pathItem.Connect); err != nil { 37 return err 38 } 39 if err := w.applyOperation(pathItem.Delete); err != nil { 40 return err 41 } 42 if err := w.applyOperation(pathItem.Get); err != nil { 43 return err 44 } 45 if err := w.applyOperation(pathItem.Head); err != nil { 46 return err 47 } 48 if err := w.applyOperation(pathItem.Options); err != nil { 49 return err 50 } 51 if err := w.applyOperation(pathItem.Patch); err != nil { 52 return err 53 } 54 if err := w.applyOperation(pathItem.Post); err != nil { 55 return err 56 } 57 if err := w.applyOperation(pathItem.Put); err != nil { 58 return err 59 } 60 } 61 return nil 62 } 63 64 func (w *includeHeaders) applyOperation(op *openapi3.Operation) error { 65 if op == nil { 66 return nil // nothing to do 67 } 68 for _, respRef := range op.Responses { 69 resp := respRef.Value 70 headersContents, ok := resp.Extensions[ExtSnykIncludeHeaders].(map[string]interface{}) 71 if !ok { 72 continue 73 } 74 ref, ok := headersContents["$ref"].(string) 75 if !ok { 76 continue 77 } 78 val := openapi3.Headers{} 79 relPath, err := w.doc.LoadReference(w.doc.RelativePath(), ref, &val) 80 if err != nil { 81 return fmt.Errorf("failed to load reference: %w", err) 82 } 83 84 if resp.Headers == nil { 85 resp.Headers = openapi3.Headers{} 86 } 87 for headerKey, headerRef := range val { 88 if _, ok := resp.Headers[headerKey]; ok { 89 continue // Response's declared headers take precedence over includes. 90 } 91 if isRelativePath(headerRef.Ref) { 92 headerRef.Ref = filepath.Join(relPath, headerRef.Ref) 93 } 94 resp.Headers[headerKey] = headerRef 95 } 96 // Remove the extension once it has been processed 97 delete(resp.Extensions, ExtSnykIncludeHeaders) 98 } 99 return nil 100 } 101 102 func isRelativePath(s string) bool { 103 if u, err := url.Parse(s); err == nil && u.Scheme != "" { 104 return false 105 } 106 return !filepath.IsAbs(s) 107 }