swagger-ui icon indicating copy to clipboard operation
swagger-ui copied to clipboard

Hide curl value for password or curl output itself?

Open lasred opened this issue 1 year ago • 2 comments

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?

lasred avatar Apr 17 '24 06:04 lasred

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

hkosova avatar May 15 '24 14:05 hkosova

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>

pbolin avatar Jul 26 '24 16:07 pbolin