Skip to content
This repository was archived by the owner on Aug 18, 2020. It is now read-only.

Commit 449fca0

Browse files
author
Ben Pope
committed
Use io-ts to validate user input.
When the typescript is converted to javascript, all type checks are lost. Instead, define the requirements in terms of io-ts types and check them at runtime.
1 parent 5631ea1 commit 449fca0

File tree

8 files changed

+169
-19
lines changed

8 files changed

+169
-19
lines changed

CHANGELOG.md

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,20 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
66
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
77

8-
## [Unreleased](https://github.com/pusher/chatkit-server-node/compare/2.4.0...HEAD)
8+
## [Unreleased](https://github.com/pusher/chatkit-server-node/compare/2.4.0...io-ts-validation)
99

1010
### Fixes
11+
1112
- Update mixin-deep and set-value for security fixes
1213

14+
### Additions
15+
16+
- Add runtime validation of arguments with io-ts
17+
18+
### Changes
19+
20+
- Requires typescript 2.8.1
21+
1322
## [2.4.0](https://github.com/pusher/chatkit-server-node/compare/2.2.0...2.4.0)
1423

1524
### Additions

package.json

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "@pusher/chatkit-server",
33
"description": "Pusher Chatkit server SDK",
4-
"version": "2.4.0",
4+
"version": "2.5.0",
55
"main": "target/src/index.js",
66
"license": "MIT",
77
"author": "Pusher",
@@ -16,9 +16,10 @@
1616
},
1717
"scripts": {
1818
"build": "rm -rf target && tsc",
19-
"test": "bash -c 'set -o pipefail; yarn test:build && yarn test:run | tap-colorize'",
19+
"test": "bash -c 'set -o pipefail; yarn test:build && yarn test:run && yarn test:unit | tap-colorize'",
2020
"test:build": "tsc -p tests",
2121
"test:run": "node tests/target/tests/main.js",
22+
"test:unit": "node tests/target/tests/unit.js",
2223
"test:withSDKBuild": "yarn build && bash -c 'set -o pipefail; yarn test:build && yarn test:run | tap-colorize'",
2324
"install-pkg": "if type yarn &> /dev/null; then yarn; else npm i; fi",
2425
"publish-please": "publish-please",
@@ -27,11 +28,15 @@
2728
"dependencies": {
2829
"@pusher/platform-node": "~0.15.0",
2930
"@types/got": "^9.4.0",
31+
"fp-ts": "1.14.4",
3032
"got": "^9.6.0",
31-
"jsonwebtoken": "^8.3.0"
33+
"io-ts": "1.5.2-",
34+
"jsonwebtoken": "^8.3.0",
35+
"unknown-ts": "^0.1.0"
3236
},
3337
"resolutions": {
34-
"**/**/lodash": "^4.17.13"
38+
"**/**/lodash": "^4.17.13",
39+
"fp-ts": "1.14.4"
3540
},
3641
"devDependencies": {
3742
"@types/tape": "^4.2.32",
@@ -41,6 +46,6 @@
4146
"publish-please": "^5.1.1",
4247
"tap-colorize": "^1.2.0",
4348
"tape": "^4.9.1",
44-
"typescript": "^2.6.1"
49+
"typescript": "^2.8.1"
4550
}
4651
}

src/chatkit.ts

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,26 @@ import {
1111
TokenWithExpiry,
1212
} from "@pusher/platform-node"
1313

14-
import { getCurrentTimeInSeconds } from "./utils"
14+
import {
15+
getCurrentTimeInSeconds,
16+
validateProperties,
17+
} from "./utils"
1518
import packageJSON from "../package.json"
19+
import * as t from 'io-ts'
20+
import "unknown-ts"
21+
22+
const NonEmptyString = t.refinement(t.string, s => s.length != 0, "NonEmptyString");
1623

1724
export interface AuthenticationOptions {
1825
userId: string
1926
authPayload?: AuthenticatePayload
2027
}
2128

22-
export interface UserIdOptions {
23-
userId: string
24-
}
29+
const UserIdOptions = t.interface({
30+
userId: NonEmptyString
31+
},"UserIdOptions");
32+
type UserIdOptions = t.TypeOf<typeof UserIdOptions>;
33+
export { UserIdOptions };
2534

2635
export interface GetRoomOptions {
2736
roomId: string
@@ -98,9 +107,14 @@ export interface GetUsersOptions {
98107
limit?: number
99108
}
100109

101-
export interface RemoveRoomRoleForUserOptions extends UserIdOptions {
102-
roomId: string
103-
}
110+
const RemoveRoomRoleForUserOptions = t.intersection([
111+
UserIdOptions,
112+
t.interface({
113+
roomId: NonEmptyString
114+
})
115+
],"RemoveRoomRoleForUserOptions");
116+
type RemoveRoomRoleForUserOptions = t.TypeOf<typeof RemoveRoomRoleForUserOptions>;
117+
export { RemoveRoomRoleForUserOptions };
104118

105119
export interface BasicAssignRoleToUserOptions {
106120
userId: string
@@ -983,6 +997,7 @@ export default class Chatkit {
983997
}
984998

985999
removeRoomRoleForUser(options: RemoveRoomRoleForUserOptions): Promise<void> {
1000+
validateProperties(options, RemoveRoomRoleForUserOptions)
9861001
return this.authorizerInstance
9871002
.request({
9881003
method: "DELETE",

src/utils.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,9 @@
1+
import { ThrowReporter } from "io-ts/lib/ThrowReporter";
2+
import { Decoder } from "io-ts";
3+
14
export const getCurrentTimeInSeconds = (): number =>
25
Math.floor(Date.now() / 1000)
6+
7+
export function validateProperties<I,A>(options: I, decoder: Decoder<I,A>) {
8+
ThrowReporter.report(decoder.decode(options))
9+
}

tests/main.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1486,6 +1486,47 @@ test("unreadCount and lastMessageAt", (t, client, end, fail) => {
14861486
)
14871487
})
14881488

1489+
test("RoomRoles", (t, client, end, fail) => {
1490+
const user = randomUser()
1491+
const role = randomString()
1492+
1493+
client
1494+
.createUser(user)
1495+
.then(() =>
1496+
client.createRoomRole({
1497+
name: role,
1498+
permissions: []
1499+
})
1500+
)
1501+
.then(() =>
1502+
client.createRoom({
1503+
creatorId: user.id,
1504+
name: randomString(),
1505+
})
1506+
)
1507+
.then(room =>
1508+
client
1509+
.assignRoomRoleToUser({
1510+
userId: user.id,
1511+
roomId: room.id,
1512+
name: role,
1513+
})
1514+
.then(() =>
1515+
client.removeRoomRoleForUser({
1516+
userId: user.id,
1517+
roomId: room.id
1518+
})
1519+
)
1520+
)
1521+
.then(() =>
1522+
client.deleteRoomRole({
1523+
name: role,
1524+
})
1525+
)
1526+
.then(end)
1527+
.catch(fail)
1528+
})
1529+
14891530
function test(
14901531
msg: string,
14911532
cb: (

tests/tsconfig.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@
55
"module": "commonjs",
66
"resolveJsonModule": true,
77
"esModuleInterop": true,
8-
"declaration": true,
98
"outDir": "target",
10-
"strict": true
9+
"strict": true,
10+
"allowJs": true
1111
},
1212
"exclude": [
1313
"node_modules",

tests/unit.js

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import tape from "tape"
2+
3+
import { validateProperties } from "../../target/src/utils.js"
4+
import { RemoveRoomRoleForUserOptions } from "../../target/src/chatkit.js"
5+
6+
const TEST_TIMEOUT = 200
7+
8+
function test(name, f) {
9+
tape(name, t => {
10+
t.timeoutAfter(TEST_TIMEOUT)
11+
f(t)
12+
})
13+
}
14+
15+
test("validateProperties", t => {
16+
validateProperties({userId: "foo", roomId: "bar"}, RemoveRoomRoleForUserOptions)
17+
t.end()
18+
})
19+
20+
test("validatePropertiesUndefinedFails", t => {
21+
t.throws(
22+
() => validateProperties({user: "foo", roomId: "bar"}, RemoveRoomRoleForUserOptions),
23+
RegExp('Invalid value undefined supplied to : RemoveRoomRoleForUserOptions/userId: NonEmptyString'))
24+
t.end()
25+
})
26+
27+
test("validatePropertiesNullFails", t => {
28+
t.throws(
29+
() => validateProperties({userId: "foo", roomId: null}, RemoveRoomRoleForUserOptions),
30+
RegExp('Invalid value null supplied to : RemoveRoomRoleForUserOptions/roomId: NonEmptyString'))
31+
t.end();
32+
})
33+
34+
test("validatePropertiesEmptyFails", t => {
35+
t.throws(
36+
() => validateProperties({userId: "foo", roomId: ""}, RemoveRoomRoleForUserOptions),
37+
RegExp('Invalid value "" supplied to : RemoveRoomRoleForUserOptions/roomId: NonEmptyString'))
38+
t.end();
39+
})
40+
41+
test("validatePropertiesWrongTypeFails", t => {
42+
t.throws(
43+
() => validateProperties({userId: "foo", roomId: 42}, RemoveRoomRoleForUserOptions),
44+
RegExp('Invalid value 42 supplied to : RemoveRoomRoleForUserOptions/roomId: NonEmptyString'))
45+
t.end();
46+
})

yarn.lock

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,13 @@
3636
"@types/node" "*"
3737

3838
"@types/got@^9.4.0":
39-
version "9.4.0"
40-
resolved "https://registry.yarnpkg.com/@types/got/-/got-9.4.0.tgz#5c8b0f5a0dc1b67e676430561a0b85a0fde1d12d"
41-
integrity sha512-18c/Bu9tpv6owaz17urNFfkzfxdmr3PoREqFpAkUasBbV9gc7OqChRj/e2f98abiJo7j++PhArfm66EFqAY4qA==
39+
version "9.6.9"
40+
resolved "https://registry.yarnpkg.com/@types/got/-/got-9.6.9.tgz#b3192188b96c871b9c67dc80d1b0336441432a38"
41+
integrity sha512-w+ZE+Ovp6fM+1sHwJB7RN3f3pTJHZkyABuULqbtknqezQyWadFEp5BzOXaZzRqAw2md6/d3ybxQJt+BNgpvzOg==
4242
dependencies:
4343
"@types/node" "*"
4444
"@types/tough-cookie" "*"
45+
form-data "^2.5.0"
4546

4647
"@types/minimatch@*":
4748
version "3.0.3"
@@ -1163,6 +1164,15 @@ forever-agent@~0.6.1:
11631164
resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91"
11641165
integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=
11651166

1167+
form-data@^2.5.0:
1168+
version "2.5.1"
1169+
resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.5.1.tgz#f2cbec57b5e59e23716e128fe44d4e5dd23895f4"
1170+
integrity sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==
1171+
dependencies:
1172+
asynckit "^0.4.0"
1173+
combined-stream "^1.0.6"
1174+
mime-types "^2.1.12"
1175+
11661176
form-data@~2.3.2:
11671177
version "2.3.3"
11681178
resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6"
@@ -1172,6 +1182,11 @@ form-data@~2.3.2:
11721182
combined-stream "^1.0.6"
11731183
mime-types "^2.1.12"
11741184

1185+
fp-ts@1.14.4, fp-ts@^1.0.0:
1186+
version "1.14.4"
1187+
resolved "https://registry.yarnpkg.com/fp-ts/-/fp-ts-1.14.4.tgz#df431e1bfe590f23ad418ca8c889880106a5008f"
1188+
integrity sha512-A20kqVBpV/VOpDmEDudb+FP/qOV+V/yEqHQQfHPHLHeX3alFM0JjX1cXNcU8+p8z5yeCkHFolKo7uJMRwUSMFA==
1189+
11751190
fragment-cache@^0.2.1:
11761191
version "0.2.1"
11771192
resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19"
@@ -1394,6 +1409,13 @@ invariant@^2.2.2:
13941409
dependencies:
13951410
loose-envify "^1.0.0"
13961411

1412+
io-ts@1.5.2-:
1413+
version "1.5.2"
1414+
resolved "https://registry.yarnpkg.com/io-ts/-/io-ts-1.5.2.tgz#e57ca59689ead86736542e27d44a9a0358d37931"
1415+
integrity sha512-97sFhiqyKqCj2iLJ3NUuAMDYh2pQm/VuSHu/zNbCdZt+I53PiQ/nkKIP7gKpM1fOAa9+xfHS675Ifit+4lP8kw==
1416+
dependencies:
1417+
fp-ts "^1.0.0"
1418+
13971419
is-accessor-descriptor@^0.1.6:
13981420
version "0.1.6"
13991421
resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6"
@@ -2588,7 +2610,7 @@ tweetnacl@^0.14.3, tweetnacl@~0.14.0:
25882610
resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64"
25892611
integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=
25902612

2591-
typescript@^2.6.1:
2613+
typescript@^2.8.1:
25922614
version "2.9.2"
25932615
resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.9.2.tgz#1cbf61d05d6b96269244eb6a3bce4bd914e0f00c"
25942616
integrity sha512-Gr4p6nFNaoufRIY4NMdpQRNmgxVIGMs4Fcu/ujdYk3nAZqk7supzBE9idmvfZIlH/Cuj//dvi+019qEue9lV0w==
@@ -2603,6 +2625,11 @@ union-value@^1.0.0:
26032625
is-extendable "^0.1.1"
26042626
set-value "^0.4.3"
26052627

2628+
unknown-ts@^0.1.0:
2629+
version "0.1.0"
2630+
resolved "https://registry.yarnpkg.com/unknown-ts/-/unknown-ts-0.1.0.tgz#b34c5ec61fe84e734fa59cd9ddc3ebc32a9ecce7"
2631+
integrity sha512-P+mV7fa1UAJ32HN+P1/eswQ33wmXt4+AuE20K0+ixzBe3t+NF51jp7gr0W6w+dSqBPvwmwSRVJIJHqaTdyf6Ag==
2632+
26062633
unset-value@^1.0.0:
26072634
version "1.0.0"
26082635
resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559"

0 commit comments

Comments
 (0)