Swagger UI Detector

  • By Miloslav Homer
  • Sun 04 December 2022
  • Updated on Sun 04 December 2022

Motivation

Swagger-ui allows anyone ... to visualize and interact with the API’s resources without having any of the implementation logic in place. It’s automatically generated from your OpenAPI (formerly known as Swagger). (quote source).

I have a long list of swagger-ui instances. Many of them are vulnerable to cross-site scripting and other issues as seen on snyk. There is a way of finding the version of an instance, but it requires clicking and looking with your eyes in it's original form.

So I wrote a tool to help me out. You can install it from pypi to check it out - pip3 install swagger-ui-detector.

Version detection

Every time you can read the doc, do so. This time the most useful resource is at the official docs.

Versions older than 3.0 are very straightforward to detect - just take a look into swagger-ui.js, use some regular expressions to extract the version number. An example:

/**
 * swagger-ui - Swagger UI is a dependency-free collection of HTML, JavaScript, and CSS assets that dynamically generate beautiful documentation from a Swagger-compliant API
 * @version v2.2.9
 * @link https://swagger.io
 * @license Apache-2.0
 */

However, this approach will fail in versions older than 3.0 for several possible reasons. We are trying to parse minimized javascript and in different versions there are different strings surrounding the exact version number. Moreover, there are multiple different version numbers in the javascript file, so you'd need to somehow differentiate between them.

But what I've noticed is the gitRevision string:

swaggerUi : Object { version: "3.1.6", gitRevision: "g786cd47", gitDirty: true,  }

It's a string starting with g followed by a hexadecimal value. Of course, it's the commit hash of the Swagger-ui version in the public github repo. And moreover, it is an unique value in this format and can be extracted with regular expressions.

The flow

The algorithm is simple:

  • clone the github repo,
  • check if the base version is <3.0 or >=3.0 (by checking the presence of swagger-ui.js file).
  • if it's <3.0, use regex to get the exact version immediately.
  • if it's >=3.0, use regex to get the commit hash, then take a look into the repo for the newest git tag that should contain the version number.
  • using the discovered version, check snyk for vulnerabilities.
  • report the results

For completeness, here's an example output:

> swagger-ui-detector --url-list http4kswag.txt

2022-06-13 15:25:23,523 [INFO] Directory for swagger-ui repo already exists.
2022-06-13 15:25:23,523 [INFO] Directory is not empty.
2022-06-13 15:25:23,525 [INFO] Directory is a valid swagger-ui dir with remote https://github.com/swagger-api/swagger-ui
2022-06-13 15:25:23,525 [INFO] Using local swagger-ui repository at ./swagger-ui
2022-06-13 15:25:23,525 [INFO] Load vulnerabilities from https://snyk.io/vuln/npm:swagger-ui ...
2022-06-13 15:25:23,903 [INFO] Loaded 14 vulnerabilities.
2022-06-13 15:25:23,903 [INFO] Got 2 URLs to try...

URL https://www.http4k.org/openapi3/ - [OK] Version v4.11.1
---------
This swagger-ui is not vulnerable.

2022-06-13 15:25:24,475 [INFO] Status: 95%, estimated 0s left.

URL https://demo.thingsboard.io/swagger-ui/ - [VULNERABLE] Version v3.52.5
---------

This swagger-ui is vulnerable to:
  - [User Interface (UI) Misrepresentation of Critical Information](https://snyk.io/vuln/SNYK-JS-SWAGGERUI-2314885)

2022-06-13 15:25:26,028 [INFO] Done.

The same and more can be found in the readme

Development history (rant warning)

There is actually another similar tool XSSwagger. Funny story: vavkamil was sitting right next to me in the office. Since the tool was not maintained it was missing newer vulnerabilities and versions.

It was actually quicker to write a similar thing from scratch. It wasn't pleasant to use at all, but it got the job done. Then I decided to make a project out of polishing it - I've never actually released a python package before.

Surprisingly, these improvements increased the dev time nearly tenfold. A short and incomplete list of these issues:

  • packaging stuff properly,
  • separating the code into classes and files,
  • writing docstrings,
  • command line interface with click,
  • testing (which I eventually cancelled as I'd need to mock many different API calls),
  • documentation (such as this blogpost and readme).

There are many more articles that would explain this general process better. What I guess I am trying to say here is that I won't be doing this type of polish regularly, as it just is not that efficient in my regular work.

To return to the fist point of this section. I expect my tool to end the same way in a few years: unmaintained and broken. And I don't think it's wrong - the tool's usefulness window has ended. I published what I did and if someone would like to use this as a head-start - go for it. But I think that when dealing with specific scenarios it might be more efficient to write it from scratch again (and better suiting for your use-case).

Possible improvements

That being said, if I have some spare time and spare will, these are the things that can (and should) be done to improve this tool:

  • switch from python-requests to aiohttp,
  • take a good look at GitHub API if this can be done without local cloning,
  • Right now the script starts with repo cloning, it fails immediately without internet,
  • ability to recieve input through stdin, add an option for single URL check,
  • enable specification of the log levels as a parameter,
  • and possibly many more.

So, let's call the current state v1 - maybe there will be a v1.1 someday.

Conclusion (TL;DR:)

  • If you have very specific needs, using python to accomplish those can be very quick and efficient.
  • Reading documentation is helpful when dealing with problems (who would have thought).
  • UX design is not my cup of tea at all.
  • Maintaining a tool that can be used by other people is a very different beast.