Mozilla Observatory GitHub Action

Time to read: 6 minutes

The Mozilla Observatory is a security tool designed to teach developers how to secure their sites.

This GitHub action is designed to help integrate it into your CI/CD workflow, to help raise awareness, and automate some security best practices.

The action is available on Github Marketplaces here: GitHub Observatory Action

Example

Quick Vercel Deploy

We can begin quickly by creating a NextJS site and deploying it to Vercel using their automated deploy system

Setup the GitHub Action

Once the site is live, we can add a github action to the created repository:

# ../../../.github/workflows/deploy.yml

name: 'web-security'
on:
  deployment_status:

jobs:
  deployment_status:
    runs-on: ubuntu-latest
    if: github.event.deployment_status.state == 'success'
    steps:
      - uses: actions/[email protected]

      - name: Test Observatory
        uses: simonireilly/[email protected]
        id: observatory
        with:
          web_host: ${{ github.event.deployment_status['target_url'] }}

      - name: Create commit comment
        uses: peter-evans/[email protected]
        with:
          body: '# Deployment Status _${{ github.event.deployment_status.state }}_ ${{ steps.observatory.outputs.observatory-report }}'

Now we can create a new Pull Request for this and see it running on GitHub.

example of github action running before and after security

Add Secure Headers

Great, so we have security in our CI/CD.

We can make use of the report link to get a full list of recommendations for security best practices. To implement these, we can use next-secure-headers.

yarn add next-secure-headers

Then into the next.config.js we can put some secure headers and re-deploy that application:

// ../next.config.js

const { createSecureHeaders } = require("next-secure-headers");

module.exports = {
  async headers() {
    return process.env.NODE_ENV === "production"
      ? [
          {
            source: "/(.*)",
            headers: createSecureHeaders({
              contentSecurityPolicy: {
                directives: {
                  objectSrc: ["'self'"],
                  scriptSrc: [
                    "'self'",
                    "'unsafe-inline'",
                    "https://www.googletagmanager.com/",
                    "https://static.hotjar.com/",
                    "https://script.hotjar.com/",
                    `https://${String(process.env.VERCEL_URL)}/`,
                    "https://blog.simonireilly.com/",
                    "https://static.cloudflareinsights.com/",
                  ],
                  defaultSrc: [
                    "'self'",
                    String(process.env.NEXT_PUBLIC_SEARCH_URL),
                    "https://vitals.vercel-insights.com/v1/vitals",
                    "https://www.google-analytics.com/",
                  ],
                  styleSrc: ["'self'", "'unsafe-inline'"],
                  imgSrc: ["'self'", "www.googletagmanager.com/"],
                  connectSrc: [
                    "'self'",
                    "https://in.hotjar.com/",
                    "https://ws20.hotjar.com/",
                    "wss://ws20.hotjar.com/api/v2/client/ws",
                    "https://vitals.vercel-insights.com/",
                    "https://www.google-analytics.com/",
                    `${String(process.env.NEXT_PUBLIC_SEARCH_URL)}/`,
                  ],
                  baseUri: "'self'",
                  formAction: "'self'",
                  frameAncestors: true,
                  frameSrc: ["https://vars.hotjar.com/"],
                },
              },
              forceHTTPSRedirect: [
                true,
                {
                  maxAge: 60 * 60 * 24 * 4,
                  includeSubDomains: true,
                },
              ],
              referrerPolicy: "same-origin",
            }),
          },
        ]
      : [];
  },
};

We get preview deploys, and the github action intercepts the target_url to ensure we can test security on only our branch.

example of github action after adding secure headers

Now we can see that we are getting a much better outcome.

Additionally we can leave this action in place, preventing security regressions, and helping everyone we collaborate with to understand some of the security best practices that matter to us.