Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: create pnpm workspace #49

Merged
merged 2 commits into from
Mar 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ root = true
end_of_line = lf
insert_final_newline = true

[*.{js,mjs,mts,json,ts,astro}]
[*.{js,mjs,mts,json,ts,astro},.hooks/*]
charset = utf-8
indent_style = tab
indent_size = 2
Expand Down
6 changes: 4 additions & 2 deletions .github/workflows/npm_publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ jobs:
- name: Install PNPM # v3.0.0
uses: pnpm/action-setup@a3252b78c470c02df07e9d59298aecedc3ccdd6d
with:
version: '8.15.3'
version: '8.15.4'
- name: Use Node.js ${{ matrix.node-version }} # v4.0.2
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8
with:
Expand All @@ -34,7 +34,9 @@ jobs:
registry-url: 'https://registry.npmjs.org'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- run: pnpm publish --provenance --no-git-checks --access public
- name: Publish to NPM registry
run: pnpm publish --provenance --no-git-checks --access public
working-directory: ./@kindspells/astro-shield
env:
NPM_CONFIG_PROVENANCE: 'true'
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
4 changes: 3 additions & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ jobs:
- name: Install PNPM # v3.0.0
uses: pnpm/action-setup@a3252b78c470c02df07e9d59298aecedc3ccdd6d
with:
version: '8.15.3'
version: '8.15.4'
- name: Use Node.js ${{ matrix.node-version }} # v4.0.2
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8
with:
Expand All @@ -41,8 +41,10 @@ jobs:
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Run Linters
working-directory: ./@kindspells/astro-shield
run: pnpm lint
- name: Run Unit Tests
working-directory: ./@kindspells/astro-shield
run: pnpm test:unit:coverage
# Disabled until we discover how to run "network-related" tests in CI
# - name: Run End-to-End Tests
Expand Down
14 changes: 7 additions & 7 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
#
# SPDX-License-Identifier: MIT

coverage/
coverage-e2e/
coverage-unit/
dist/
tests/playground/*.mjs
generated/
node_modules/
**/coverage/
**/coverage-e2e/
**/coverage-unit/
**/dist/
**/tests/playground/*.mjs
**/generated/
**/node_modules/
10 changes: 8 additions & 2 deletions .hooks/pre-commit
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,11 @@
set -eu
set -o pipefail

pnpm lint
pnpm test:unit
check_astro_shield () {
cd "@kindspells/astro-shield";
pnpm lint;
pnpm test:unit;
cd -;
}

check_astro_shield;
21 changes: 21 additions & 0 deletions @kindspells/astro-shield/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2024 KindSpells Labs S.L.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
250 changes: 250 additions & 0 deletions @kindspells/astro-shield/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,250 @@
<!--
SPDX-FileCopyrightText: 2024 KindSpells Labs S.L.

SPDX-License-Identifier: CC-BY-4.0
-->
# Astro-Shield

[![NPM Version](https://img.shields.io/npm/v/%40kindspells%2Fastro-shield)](https://www.npmjs.com/package/@kindspells/astro-shield)
![NPM Downloads](https://img.shields.io/npm/dw/%40kindspells%2Fastro-shield)
![GitHub commit activity](https://img.shields.io/github/commit-activity/w/kindspells/astro-shield)
![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/kindspells/astro-shield/tests.yml)
[![Socket Badge](https://socket.dev/api/badge/npm/package/@kindspells/astro-shield)](https://socket.dev/npm/package/@kindspells/astro-shield)

## Introduction

This library will help you to compute the subresource integrity hashes for your
JS scripts and CSS stylesheets.

It works by installing an Astro hook that runs once the build step is done. This
hook performs 3 steps:
1. Computes the Subresource Integrity hashes for your scripts and styles.
2. Modifies the generated HTML to include the integrity hashes.
3. In case you specified a filepath for your SRI hashes module, it will generate
(or update) a module that exports the associated SRI hashes, so you can use
them later for other purposes, such as configuring your
`Content-Security-Policy` headers.

## How to install

```bash
# With NPM
npm install --save-dev @kindspells/astro-shield

# With Yarn
yarn add --dev @kindspells/astro-shield

# With PNPM
pnpm add --save-dev @kindspells/astro-shield
```

## How to use

In your `astro.config.mjs` file:

```javascript
import { resolve } from 'node:path'

import { defineConfig } from 'astro/config'
import { shield } from '@kindspells/astro-shield'

const rootDir = new URL('.', import.meta.url).pathname

export default defineConfig({
integrations: [
shield({
sri: {
// Enables SRI hashes generation for statically generated pages
enableStatic: true, // true by default

// Enables a middleware that generates SRI hashes for dynamically
// generated pages
enableMiddleware: false, // false by default

// This is the path where we'll generate the module containing the SRI
// hashes for your scripts and styles. There's no need to pass this
// parameter if you don't need this data, but it can be useful to
// configure your CSP policies.
hashesModule: resolve(rootDir, 'src', 'utils', 'sriHashes.mjs'),

// For SSR content, Cross-Origin scripts must be explicitly allow-listed
// by URL in order to be allowed by the Content Security Policy.
//
// Defaults to []
scriptsAllowListUrls: [
'https://code.jquery.com/jquery-3.7.1.slim.min.js',
],

// For SSR content, Cross-Origin styles must be explicitly allow-listed
// by URL in order to be allowed by the Content Security Policy.
//
// Defaults to []
stylesAllowListUrls: [
'https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css',
],

/**
* Inline styles are usually considered unsafe because they could make it
* easier for an attacker to inject CSS rules in dynamic pages. However, they
* don't pose a serious security risk for _most_ static pages.
*
* You can disable this option in case you want to enforce a stricter policy.
*
* @type {'all' | 'static' | false}
*
* Defaults to 'all'.
*/
allowInlineStyles: 'all',

/**
* Inline scripts are usually considered unsafe because they could make it
* easier for an attacker to inject JS code in dynamic pages. However, they
* don't pose a serious security risk for _most_ static pages.
*
* You can disable this option in case you want to enforce a stricter policy.
*
* @type {'all' | 'static' | false}
*
* Defaults to 'all'.
*/
allowInlineScript: 'all',
},

// - If set, it controls how the security headers will be generated in the
// middleware.
// - If not set, no security headers will be generated in the middleware.
securityHeaders: {
// For now, we can only control CSP headers, but we'll add more options
// in the future.
// - If set, it controls how the CSP (Content Security Policy) header will be
// generated in the middleware.
// - If not set, no CSP header will be generated in the middleware.
contentSecurityPolicy: {
// - If set, it controls the "default" CSP directives (they can be overriden
// at runtime).
// - If not set, the middleware will use a minimal set of default directives.
cspDirectives: {
'default-src': "'none'",
}
}
}
})
]
})
```

### Generating Content-Security-Policy Headers

You can enable automated CSP headers generation by setting the option
`securityHeaders.contentSecurityPolicy` (it can be an empty object if you don't
need to customise any specific behavior, but it must be defined).

Besides enabling CSP, you can also configure its directives to some extent, via
the `cspDirectives` option.

> [!IMPORTANT]
> It is advisable to set the option `sriHashesModule` in case your dynamic pages
> include static JS or CSS resources.
>
> Also, do not explicitly disable the `enableStatic_SRI` option if you want
> support for those static assets).

### Accessing metadata generated at build time

Once you run `astro build`, `@kindspells/astro-shield` will analyse the static
output and generate a new module that exports the SRI hashes, so you can use
them in your CSP headers.

Here you can see an example of how the generated module looks:

```javascript
// Do not edit this file manually

export const inlineScriptHashes = /** @type {string[]} */ ([])

export const inlineStyleHashes = /** @type {string[]} */ ([
'sha256-VC84dQdO3Mo7nZIRaNTJgrqPQ0foHI8gdp/DS+e9/lk=',
])

export const extScriptHashes = /** @type {string[]} */ ([
'sha256-+aSouJX5t2z1jleTbCvA9DS7+ag/F4e4ZpB/adun4Sg=',
])

export const extStyleHashes = /** @type {string[]} */ ([
'sha256-iwd3GNfA+kImEozakD3ZZQSZ8VVb3MFBOhJH6dEMnDE=',
])

export const perPageSriHashes =
/** @type {Record<string, { scripts: string[]; styles: string [] }>} */ ({
'index.html': {
scripts: [
'sha256-+aSouJX5t2z1jleTbCvA9DS7+ag/F4e4ZpB/adun4Sg=',
],
styles: [
'sha256-VC84dQdO3Mo7nZIRaNTJgrqPQ0foHI8gdp/DS+e9/lk='
],
},
'about.html': {
scripts: [
'sha256-+aSouJX5t2z1jleTbCvA9DS7+ag/F4e4ZpB/adun4Sg=',
],
styles: [
'sha256-iwd3GNfA+kImEozakD3ZZQSZ8VVb3MFBOhJH6dEMnDE=',
],
},
})
```

> [!IMPORTANT]
> If your website is very small or it relies on
> [View Transitions](https://developer.mozilla.org/en-US/docs/Web/API/View_Transitions_API),
> then it's best to rely on the `inlineScriptHashes`, `inlineStyleHashes`,
> `extScriptHashes` and `extStyleHashes` values.

> [!IMPORTANT]
> If you don't rely on View Transitions and you care about minimising the size
> of your CSP headers, then you can rely on the `perPageSriHashes` exported
> value.

## Known limitations

- ⚠️ In case your SSR (dynamic) pages refer to static `.js` or `.css` files, and
any of these resources change, then you will need to run the `astro build`
command **two consecutive times** (Astro-Shield will emit a warning message
telling you about it).

- The SRI hashes will be regenerated only when running `astro build`. This means
that if you need them to be up to date when you run `astro dev`, then you will
have to manually run `astro build`.

- In the context of Content-Security-Policy: When a script is loaded with a
_static_ import rather than directly included with a `<script>` tag, having
its hash present in the `script-src` directive is not enough to ensure that
the browser will accept it.

This means that, for now, it is advisable to add `'self'` to the `script-src`
directive (adding `'strict-dynamic'` does not help either).

## Some guarantees for peace of mind

Astro generates files in a very deterministic way, which means that for both JS
and CSS files:
- Their pseudo-random names are stable across different builds
- The files' contents do not change from build to build (unless, of course, we
change them on purpose), so their hashes are stable as well (this is nice
for hot reloading, which does not trigger the logic of this integration).

## Other Relevant Guidelines

- [Code of Conduct](https://github.com/KindSpells/astro-shield?tab=coc-ov-file)
- [Contributing Guidelines](https://github.com/KindSpells/astro-shield/blob/main/CONTRIBUTING.md)
- [Security Policy](https://github.com/KindSpells/astro-shield/security/policy)

## Main Contributors

This library has been created and is being maintained by
[KindSpells Labs](https://kindspells.dev/?utm_source=github&utm_medium=astro_sri_scp&utm_campaign=floss).

## License

This library is released under [MIT License](https://github.com/KindSpells/astro-shield?tab=MIT-1-ov-file).
File renamed without changes.
File renamed without changes.
Loading
Loading