swagger-api / swagger-ui

Swagger UI is a collection of HTML, JavaScript, and CSS assets that dynamically generate beautiful documentation from a Swagger-compliant API.
https://swagger.io
Apache License 2.0
26.66k stars 8.97k forks source link

Hide curl value for password or curl output itself? #9833

Open lasred opened 7 months ago

lasred commented 7 months ago

Hi team,

This feature request is very similar to this one - https://github.com/swagger-api/swagger-ui/issues/8320

We're looking for a way to hide the password or the curl output itself

Is there a feature request for this? If there is, what is the priority of this request?
hkosova commented 6 months ago

To hide the curl commands, you can add this plugin to your Swagger UI initialization code: https://github.com/swagger-api/swagger-ui/issues/5020#issuecomment-653756698

pbolin commented 4 months ago

This is kind of a 'there I fixed it' with duct tape and a hammer solution, but you can do something like this to redact apikey based auth, and should be modifiable for other methods if needed. Also, does not work with custom curlOptions. Posting just in case it helps someone else. You need to replace YOUR_AUTH_HEADER_NAME with your auth header name, and cdbspec with your json openapi spec. This also leaves two additional tabs for poweshell and cmd but blanks them out (couldnt figure out how to remove them entirely yet)

    <script>
    window.onload = function() {
      // Add config to Request Snippets Configuration with an unique key like "node_native" 
      const snippetConfig = {
        requestSnippetsEnabled: true,
        requestSnippets: {
          generators: {
            "curl_bash": {
              title: "cURL (bash)",
              syntax: "bash"
            }
          }
        }
      }

      const NoPowershellPlugin = {
        fn: {
          requestSnippetGenerator_curl_powershell: (request) => null
        }
      }

      const NoCmdPlugin = {
        fn: {
          requestSnippetGenerator_curl_cmd: (request) => null
        }
      }

      const OverrideBashCurlPlugin = {
        fn: {
          // use `requestSnippetGenerator_` + key from config (node_native) for generator fn
          requestSnippetGenerator_curl_bash: (request) => {

            const escapeShell = (str) => {
              if (str === "-d ") {
                return str
              }
              // eslint-disable-next-line no-useless-escape
              if (!/^[_\/-]/g.test(str))
                return ("'" + str
                  .replace(/'/g, "'\\''") + "'")
              else
                return str
            }

            const curlify = (request, escape, newLine, ext = "") => {
              let isMultipartFormDataRequest = false
              let curlified = ""
              const addWords = (...args) => curlified += " " + args.map(escape).join(" ")
              const addWordsWithoutLeadingSpace = (...args) => curlified += args.map(escape).join(" ")
              const addNewLine = () => curlified += ` ${newLine}`
              const addIndent = (level = 1) => curlified += "  ".repeat(level)
              let headers = request.get("headers")
              curlified += "curl" + ext

              const curlOptions = request.get("curlOptions")
              //if (List.isList(curlOptions) && !curlOptions.isEmpty()) {
                //addWords(...request.get("curlOptions"))
              //}

              addWords("-X", request.get("method"))

              addNewLine()
              addIndent()
              addWordsWithoutLeadingSpace(`${request.get("url")}`)

              if (headers && headers.size) {
                for (let p of request.get("headers").entries()) {
                  addNewLine()
                  addIndent()
                  let [h, v] = p
                  if (h == "YOUR_AUTH_HEADER_NAME") {
                    v = "REDACTED"
                  }
                  addWordsWithoutLeadingSpace("-H", `${h}: ${v}`)
                  isMultipartFormDataRequest = isMultipartFormDataRequest || /^content-type$/i.test(h) && /^multipart\/form-data$/i.test(v)
                }
              }

              const body = request.get("body")
              if (body) {
                if (isMultipartFormDataRequest && ["POST", "PUT", "PATCH"].includes(request.get("method"))) {
                  for (let [k, v] of body.entrySeq()) {
                    let extractedKey = extractKey(k)
                    addNewLine()
                    addIndent()
                    addWordsWithoutLeadingSpace("-F")

                    /**
                     * SwaggerClient produces specialized sub-class of File class, that only
                     * accepts string data and retain this data in `data`
                     * public property throughout the lifecycle of its instances.
                     *
                     * This sub-class is exclusively used only when Encoding Object
                     * is defined within the Media Type Object (OpenAPI 3.x.y).
                     */
                    if (v instanceof win.File && typeof v.valueOf() === "string") {
                      addWords(`${extractedKey}=${v.data}${v.type ? `;type=${v.type}` : ""}`)
                    } else if (v instanceof win.File) {
                      addWords(`${extractedKey}=@${v.name}${v.type ? `;type=${v.type}` : ""}`)
                    } else {
                      addWords(`${extractedKey}=${v}`)
                    }
                  }
                } else if(body instanceof win.File) {
                  addNewLine()
                  addIndent()
                  addWordsWithoutLeadingSpace(`--data-binary '@${body.name}'`)
                } else {
                  addNewLine()
                  addIndent()
                  addWordsWithoutLeadingSpace("-d ")
                  let reqBody = body
                  if (!Map.isMap(reqBody)) {
                    if (typeof reqBody !== "string") {
                      reqBody = JSON.stringify(reqBody)
                    }
                    addWordsWithoutLeadingSpace(reqBody)
                  } else {
                    addWordsWithoutLeadingSpace(getStringBodyOfMap(request))
                  }
                }
              } else if (!body && request.get("method") === "POST") {
                addNewLine()
                addIndent()
                addWordsWithoutLeadingSpace("-d ''")
              }
              return curlified
            }
            return curlify(request, escapeShell, "\\\n")
          }
        }
      }

      const urlParams = new URLSearchParams(window.location.search);
      const product = urlParams.get('server')

      var spec = cdbspec;

      // Begin Swagger UI call region
      const ui = SwaggerUIBundle({
        url: "https://petstore.swagger.io/v2/swagger.json",
        spec: spec,
        dom_id: '#swagger-ui',
        deepLinking: true,
        docExpansion: 'none',
        filter: true,
        operationsSorter: 'method',
        tagsSorter: 'alpha',
        presets: [
          SwaggerUIBundle.presets.apis,
          SwaggerUIStandalonePreset
        ],
        plugins: [
          SwaggerUIBundle.plugins.DownloadUrl,
          OverrideBashCurlPlugin,
          NoPowershellPlugin,
          NoCmdPlugin
        ],
        layout: "StandaloneLayout",
        ...snippetConfig
      })
      // End Swagger UI call region

      window.ui = ui
    }
    </script>