pb33f / wiretap

The world's coolest API Validation and compliance tool. Validate APIs against OpenAPI specifications and much more
https://pb33f.io/wiretap/
Other
125 stars 18 forks source link

UI is not loading css and resulting in error due to strict mime type check via helmet #122

Closed suneel-lean closed 6 months ago

suneel-lean commented 6 months ago
<service.cust_domain.com>/:1 Refused to apply style from 'https://<service.cust_domain.com>/assets/style-ea4d1e1d.css' because its MIME type ('text/plain') is not a supported stylesheet MIME type, and strict MIME checking is enabled.
index-e436dcb3.js:6599 WebSocket connection to 'ws://localhost:9092/ranch' failed: 
_createWebSocket @ index-e436dcb3.js:6599
index-e436dcb3.js:7253 new version of wiretap detected, wiping application state.
<service.cust_domain.com>/:1 Refused to apply style from 'https://<service.cust_domain.com>/assets/style-ea4d1e1d.css' because its MIME type ('text/plain') is not a supported stylesheet MIME type, and strict MIME checking is enabled.

UI without css loading:

image

I am using helmet here also:

app.use(helmet());

lib version used:

"@pb33f/wiretap": "^0.1.13"

Note: The above is deployed in kubernetes

Any help would be fruitful

suneel-lean commented 6 months ago

Also with regards to the websocket even if the websocket is exposed at the docker level and once deployed to kubernetes, we set the dns that is the host address, and if we check the logs we can see that it is hitting the localhost:9092/ranch externally which doesn't work also

Below are the commands which are used to run the wiretap:

"wiretap": "wiretap -u 'http://localhost:3000' -s './resources/open.api.spec/spec.yaml' --stream-report --report-filename './reports/conformance-report.json'",

the above is in the scripts of package.json

and command used to run:

npm run service # this is api service running at 3000 port
and
npm run wiretap this is running in default ports

and I have exposed the ports at the docker level as well:

# Expose multiple ports
EXPOSE 9091 3000 9000 9090 9092
image
daveshanley commented 6 months ago
<service.cust_domain.com>/:1 Refused to apply style from 'https://<service.cust_domain.com>/assets/style-ea4d1e1d.css' because its MIME type ('text/plain') is not a supported stylesheet MIME type, and strict MIME checking is enabled.
index-e436dcb3.js:6599 WebSocket connection to 'ws://localhost:9092/ranch' failed: 
_createWebSocket @ index-e436dcb3.js:6599
index-e436dcb3.js:7253 new version of wiretap detected, wiping application state.
<service.cust_domain.com>/:1 Refused to apply style from 'https://<service.cust_domain.com>/assets/style-ea4d1e1d.css' because its MIME type ('text/plain') is not a supported stylesheet MIME type, and strict MIME checking is enabled.

I am using helmet here also:

What is helmet?

Also this not something I can solve

<service.cust_domain.com>/:1 Refused to apply style from 'https://<service.cust_domain.com>/assets/style-ea4d1e1d.css' because its MIME type ('text/plain') is not a supported stylesheet MIME type, and strict MIME checking is enabled.

This looks like a local OS configuration issue. That's the content type returned by the OS. It's not set by wiretap.

For example, my OS is correctly configured to know that a .css file resolves to text/css and so when wiretap serves the file, it sets the correct response headers. This is handled automatically and not something controlled by the app, but rather the OS serving it.

Here is what I see.

Screenshot 2024-05-16 at 10 45 39 AM

Notice the Content-Type of the response headers?

daveshanley commented 6 months ago

Also with regards to the websocket even if the websocket is exposed at the docker level and once deployed to kubernetes, we set the dns that is the host address, and if we check the logs we can see that it is hitting the localhost:9092/ranch externally which doesn't work also

# Expose multiple ports
EXPOSE 9091 3000 9000 9090 9092

To change the websocket host and port (in case you need control over it, use the -v and -w flags.

  -v, --ws-host string                    Set the backend hostname for wiretap, for remotely deployed service (default "localhost")
  -w, --ws-port string                    Set port on which to serve the monitor UI websocket (default is 9092)

Also if you can't connect to those ports via docker, have you actually mapped the docker exposed ports to the internal ports?

docker run -p 9090:9090 -p 9091:9091 -p 9092:9092 --rm -v $PWD:/work:rw  \
pb33f/wiretap -u https://somehostoutthere.com
suneel-lean commented 6 months ago

the below is the docker command being used to test locally and the proper exposing and proper mapping also have been done:

docker run -p 9090:9090 -p 9091:9091 -p 3000:3000 -p 9092:9092 --name <container_name> <image_name>:<version_tag>

Below is my docker file:

# Use the official Node.js image as the base image
FROM node:20.12.2-bullseye

# Set the working directory in the container
WORKDIR /usr/src/app

# Copy package.json
COPY package.json ./

# Install Wiretap globally
RUN npm install -g @pb33f/wiretap

# Install dependencies
RUN npm install

# Copy the rest of the application code to the container
COPY . .

# Create a script to run Wiretap, conformance.js, and start the server
COPY entrypoint.sh /usr/src/app/entrypoint.sh
RUN chmod +x /usr/src/app/entrypoint.sh

# Expose multiple ports
EXPOSE 9091 3000 9000 9090 9092

# Set the default command to run the script
CMD ["/usr/src/app/entrypoint.sh"]

and the entrypoint script:

#!/bin/bash

# Run your service in the background
nohup npm run service &
PID1=$!

# Run the wiretap command in the background
nohup npm run wiretap &
PID2=$!

# Wait for any of the background processes to finish, keeping the container alive
wait $PID1 $PID2

and with regards to content type and helmet: this is the lib I am using : helmet below is my server file:

import express from "express";
import { createLightship } from "lightship";
import conformanceRoutes from "./routes/conformance.mjs";
import errorHandler from "./middleware/error.handler.mjs";
import dataRoutes from "./routes/data.mjs";
import logger from "./config/logger.js";
import helmet from "helmet";
import cors from "cors";

const app = express();
const port = process.env.PORT || 3000;

try {
  /* Middleware */
  app.use(helmet());
  app.use(cors());
  app.use(express.urlencoded({ extended: true }));
  app.use(
    express.json({
      limit: "1mb",
    }),
  );

  /* Lightship Integration */
  const lightship = await createLightship({
    detectKubernetes: false,
    port: 9000,
  });

  /* Routes */
  app.use("/api/conformance", conformanceRoutes);
  app.use("/", dataRoutes);

  /* Error Middleware */
  app.use(errorHandler);

  /* Start the server */
  app.listen(port, () => {
    logger.info(`Server listening on port ${port}`);
    // Signal to Kubernetes that the server is ready
    lightship.signalReady();
  });
} catch (error) {
  logger.error(error);
}

and you were mentioning, we can use the below:

  -v, --ws-host string                    Set the backend hostname for wiretap, for remotely deployed service (default "localhost")
  -w, --ws-port string                    Set port on which to serve the monitor UI websocket (default is 9092)

Can you give an example for the above on changing the host string. ⏫ as I cannot find the config with -v here in the doc but I can see the -w which is fine

the issue with the styling not appearing only appears when deployed to cloud and locally in docker it is perfectly working fine and no issues - this is something that I will look in to and dig deeper and find out the issue and fix it and share it here as well.

suneel-lean commented 6 months ago

Ok, I was able to make some changes and I was able to get the host for ws changed, but now the problem is ws is insecure and we need wss, could there anything done from your end for this: https://gist.github.com/jsdevtom/7045c03c021ce46b08cb3f41db0d76da

suneel-lean commented 6 months ago

is there a way to control the secure and insecure websocket connection from the end user as I have almost close to fruition: below is the command I am using:

"wiretap -u 'http://localhost:3000' -v '<custom_domain>' -w '443' -s './resources/open.api.spec/spec_data.yaml' --stream-report --report-filename './reports/conformance-report.json'"

in the above we are using 443 port which to pointing to ingress and ingress internally is routed to 9092 websocket port

daveshanley commented 6 months ago

To run wiretap over a secure connection (TLS / WSS) then you need to provide a TLS certificate using the -n or --cert

-n, --cert string Set the path to the TLS certificate to use for TLS/HTTPS

This will run wiretap over TLS and WSS and enable HTTP/2