Skip to content

Commit

Permalink
feat: better tsdoc comments and documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
zeyu2001 authored Jul 25, 2024
1 parent 4360c58 commit be37c29
Show file tree
Hide file tree
Showing 13 changed files with 192 additions and 29 deletions.
11 changes: 11 additions & 0 deletions apps/docs/.vitepress/config.mts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { defineConfig } from "vitepress";
import { scanDir } from "./utils";

export default defineConfig({
lang: "en-US",
Expand All @@ -19,5 +20,15 @@ export default defineConfig({
socialLinks: [
{ icon: "github", link: "https://github.com/opengovsg/starter-kitty" },
],

docFooter: {
prev: false,
next: false,
},

sidebar: {
"/packages/": scanDir("packages"),
"/api/": scanDir("api"),
},
},
});
44 changes: 44 additions & 0 deletions apps/docs/.vitepress/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import fs from "fs";
import path from "path";

export const scanDir = (dir: string) => {
let res = fs
.readdirSync(path.resolve(__dirname, `../${dir}`))
.filter((item) => !item.startsWith("."));
if (res) {
const arr = [];
for (let item of res) {
const parts = item.split(".");

let currItem = arr;
let currentPath = "";
while (parts.length > 2) {
const part = parts.shift();
currentPath += (currentPath === "" ? "" : ".") + part;
const found = currItem.find((item) => item.text === part);
if (found) {
currItem = found.items;
} else {
const newItem = {
text: part,
items: [],
link: path.join(dir, currentPath),
};
currItem.push(newItem);
currItem = newItem.items;
}
}
const found = currItem.find((item) => item.text === parts[0]);
if (!found)
currItem.push({
text: parts[0],
items: [],
link: path.join(dir, item),
});
}
return arr;
} else {
console.warn("No files found in the directory");
return [];
}
};
2 changes: 1 addition & 1 deletion apps/docs/api/starter-kitty-validators.options.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ Whitelist

</td><td>

_(Optional)_ The list of allowed protocols and hostnames for URL validation.
_(Optional)_ The list of allowed protocols and hostnames for URL validation. The default whitelist allows only `http` and `https` protocols, and does not restrict hostnames.


</td></tr>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

## Options.whitelist property

The list of allowed protocols and hostnames for URL validation.
The list of allowed protocols and hostnames for URL validation. The default whitelist allows only `http` and `https` protocols, and does not restrict hostnames.

**Signature:**

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ Creates a new UrlValidator instance. If no options are provided, the validator w
},
}
```
In most cases, you should provide your own whitelist with a list of allowed hostnames to prevent open redirects.

**Signature:**

Expand Down
1 change: 1 addition & 0 deletions apps/docs/api/starter-kitty-validators.urlvalidator.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ Creates a new UrlValidator instance. If no options are provided, the validator w
},
}
```
In most cases, you should provide your own whitelist with a list of allowed hostnames to prevent open redirects.


</td></tr>
Expand Down
3 changes: 3 additions & 0 deletions apps/docs/packages/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Packages

- [`@opengovsg/starter-kitty-validators`](./validators.md): Common input validators.
61 changes: 61 additions & 0 deletions apps/docs/packages/validators.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# @opengovsg/starter-kitty-validators

## Installation

```bash
npm i --save @opengovsg/starter-kitty-validators
```

### Example

```javascript
import { UrlValidator } from '@opengovsg/starter-kitty-validators'

const validator = new UrlValidator({
whitelist: {
protocols: ['http', 'https', 'mailto'],
hosts: ['open.gov.sg'],
},
})
```

Validating a post-login redirect URL provided in a query parameter:

```javascript
try {
router.push(validator.parse(redirectUrl))
} catch (error) {
router.push('/home')
}
```

Consider using the validator as part of a Zod schema to validate the URL and fall back to a default URL if the URL is invalid.

```javascript
const baseUrl = getBaseUrl()

const validator = new UrlValidator({
baseOrigin: new URL(baseUrl).origin,
whitelist: {
protocols: ['http', 'https'],
hosts: [new URL(baseUrl).host],
},
})

export const callbackUrlSchema = z
.string()
.optional()
.default(HOME)
.transform((url, ctx) => {
try {
return validator.parse(url)
} catch (error) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: (error as Error).message,
})
return z.NEVER
}
})
.catch(new URL(HOME, baseUrl))
```
87 changes: 63 additions & 24 deletions packages/validators/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,66 @@

This package provides a set of validators that provide sensible defaults to prevent common security vulnerabilities.

## Installation

```bash
npm i --save @opengovsg/starter-kitty-validators
```

### Example

```javascript
import { UrlValidator } from '@opengovsg/starter-kitty-validators'

const validator = new UrlValidator({
whitelist: {
protocols: ['http', 'https', 'mailto'],
hosts: ['open.gov.sg'],
},
})
```

Validating a post-login redirect URL provided in a query parameter:

```javascript
try {
router.push(validator.parse(redirectUrl))
} catch (error) {
router.push('/home')
}
```

Consider using the validator as part of a Zod schema to validate the URL and fall back to a default URL if the URL is invalid.

```javascript
const baseUrl = getBaseUrl()

const validator = new UrlValidator({
baseOrigin: new URL(baseUrl).origin,
whitelist: {
protocols: ['http', 'https'],
hosts: [new URL(baseUrl).host],
},
})

export const callbackUrlSchema = z
.string()
.optional()
.default(HOME)
.transform((url, ctx) => {
try {
return validator.parse(url)
} catch (error) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: (error as Error).message,
})
return z.NEVER
}
})
.catch(new URL(HOME, baseUrl))
```

## Class: `UrlValidator`

Validates URLs against a whitelist of allowed protocols and hostnames, preventing open redirects, XSS, SSRF, and other security vulnerabilities.
Expand All @@ -10,12 +70,12 @@ Validates URLs against a whitelist of allowed protocols and hostnames, preventin

`options?`: `<Object>`

- `baseOrigin`: `<string>` - The base origin to use for relative URLs. If no base origin is provided, relative URLs will be considered invalid.
- `baseOrigin?`: `<string>` - The base origin to use for relative URLs. If no base origin is provided, relative URLs will be considered invalid.

An origin does not include the path or query parameters. For example, a valid base origin is `https://example.com` or `http://localhost:3000`.

- `whitelist`: `<Object>`
- `protocols`: `<string[]>` - A list of allowed protocols. If no protocols are provided, the validator will use the default protocols: `['http', 'https']`.
- `whitelist?`: `<Object>`
- `protocols`: `<string[]>` - A list of allowed protocols.

**Caution: allowing `javascript` or `data` protocols can lead to XSS vulnerabilities.**
- `hostnames`: `<string[]>` - A list of allowed hostnames. If no hostnames are provided, the validator will allow any hostname.
Expand All @@ -37,24 +97,3 @@ If no options are provided, the validator will use the default options:
- `url`: `<string>` - The URL to parse.
- Returns: `<URL>` - The parsed URL object.
- Throws: `<UrlValidationError>` - If the URL is invalid or unsafe.

### Example

```javascript
const validator = new UrlValidator({
whitelist: {
protocols: ['http', 'https', 'mailto'],
hosts: ['open.gov.sg'],
},
})
```

Validating a post-login redirect URL provided in a query parameter:

```javascript
try {
router.push(validator.parse(redirectUrl))
} catch (error) {
router.push('/home')
}
```
2 changes: 1 addition & 1 deletion packages/validators/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@opengovsg/starter-kitty-validators",
"version": "1.0.1",
"version": "1.0.2",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"files": [
Expand Down
2 changes: 2 additions & 0 deletions packages/validators/src/url/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ export class UrlValidator {
* }
* ```
*
* In most cases, you should provide your own whitelist with a list of allowed hostnames to prevent open redirects.
*
* @param options - The options to use for validation
* @throws {@link OptionsError} If the options are invalid
*
Expand Down
1 change: 1 addition & 0 deletions packages/validators/src/url/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export interface Options {
baseOrigin?: string
/**
* The list of allowed protocols and hostnames for URL validation.
* The default whitelist allows only `http` and `https` protocols, and does not restrict hostnames.
*
* @example
* ```
Expand Down
4 changes: 2 additions & 2 deletions packages/validators/temp/starter-kitty-validators.api.json
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@
{
"kind": "PropertySignature",
"canonicalReference": "@opengovsg/starter-kitty-validators!Options#whitelist:member",
"docComment": "/**\n * The list of allowed protocols and hostnames for URL validation.\n *\n * @example\n * ```\n * {\n * protocols: ['http', 'https'],\n * hosts: ['example.com']\n * }\n * ```\n *\n */\n",
"docComment": "/**\n * The list of allowed protocols and hostnames for URL validation. The default whitelist allows only `http` and `https` protocols, and does not restrict hostnames.\n *\n * @example\n * ```\n * {\n * protocols: ['http', 'https'],\n * hosts: ['example.com']\n * }\n * ```\n *\n */\n",
"excerptTokens": [
{
"kind": "Content",
Expand Down Expand Up @@ -392,7 +392,7 @@
{
"kind": "Constructor",
"canonicalReference": "@opengovsg/starter-kitty-validators!UrlValidator:constructor(1)",
"docComment": "/**\n * Creates a new UrlValidator instance. If no options are provided, the validator will use the default options:\n * ```ts\n * {\n * whitelist: {\n * protocols: ['http', 'https'],\n * },\n * }\n * ```\n *\n * @param options - The options to use for validation\n *\n * @throws\n *\n * {@link OptionsError} If the options are invalid\n *\n * @public\n */\n",
"docComment": "/**\n * Creates a new UrlValidator instance. If no options are provided, the validator will use the default options:\n * ```ts\n * {\n * whitelist: {\n * protocols: ['http', 'https'],\n * },\n * }\n * ```\n *\n * In most cases, you should provide your own whitelist with a list of allowed hostnames to prevent open redirects.\n *\n * @param options - The options to use for validation\n *\n * @throws\n *\n * {@link OptionsError} If the options are invalid\n *\n * @public\n */\n",
"excerptTokens": [
{
"kind": "Content",
Expand Down

0 comments on commit be37c29

Please sign in to comment.