Add Zod for runtime and compile-time type checking, use it in logging code
This commit is contained in:
parent
c711fbe0c0
commit
eb97c1194a
6 changed files with 58 additions and 56 deletions
|
@ -3058,3 +3058,27 @@ Signal Desktop makes use of the following open source projects.
|
||||||
of your accepting any such warranty or additional liability.
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
END OF TERMS AND CONDITIONS
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
## zod
|
||||||
|
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2020 Colin McDonnell
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
|
@ -154,7 +154,8 @@
|
||||||
"underscore": "1.9.0",
|
"underscore": "1.9.0",
|
||||||
"uuid": "3.3.2",
|
"uuid": "3.3.2",
|
||||||
"websocket": "1.0.28",
|
"websocket": "1.0.28",
|
||||||
"zkgroup": "https://github.com/signalapp/signal-zkgroup-node.git#2d7db946cc88492b65cc66e9aa9de0c9e664fd8d"
|
"zkgroup": "https://github.com/signalapp/signal-zkgroup-node.git#2d7db946cc88492b65cc66e9aa9de0c9e664fd8d",
|
||||||
|
"zod": "1.11.13"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "7.7.7",
|
"@babel/core": "7.7.7",
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
// Copyright 2018-2021 Signal Messenger, LLC
|
// Copyright 2018-2021 Signal Messenger, LLC
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
import * as z from 'zod';
|
||||||
import FormData from 'form-data';
|
import FormData from 'form-data';
|
||||||
import { gzip } from 'zlib';
|
import { gzip } from 'zlib';
|
||||||
import pify from 'pify';
|
import pify from 'pify';
|
||||||
|
@ -9,28 +10,21 @@ import { getUserAgent } from '../util/getUserAgent';
|
||||||
|
|
||||||
const BASE_URL = 'https://debuglogs.org';
|
const BASE_URL = 'https://debuglogs.org';
|
||||||
|
|
||||||
const isObject = (value: unknown): value is Record<string, unknown> =>
|
const tokenBodySchema = z
|
||||||
typeof value === 'object' && !Array.isArray(value) && Boolean(value);
|
.object({
|
||||||
|
fields: z.record(z.unknown()),
|
||||||
|
url: z.string(),
|
||||||
|
})
|
||||||
|
.nonstrict();
|
||||||
|
|
||||||
const parseTokenBody = (
|
const parseTokenBody = (
|
||||||
body: unknown
|
rawBody: unknown
|
||||||
): { fields: Record<string, unknown>; url: string } => {
|
): { fields: Record<string, unknown>; url: string } => {
|
||||||
if (!isObject(body)) {
|
const body = tokenBodySchema.parse(rawBody);
|
||||||
throw new Error('Token body is not an object');
|
|
||||||
}
|
|
||||||
|
|
||||||
const { fields, url } = body as Record<string, unknown>;
|
|
||||||
|
|
||||||
if (!isObject(fields)) {
|
|
||||||
throw new Error('Token body\'s "fields" key is not an object');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof url !== 'string') {
|
|
||||||
throw new Error('Token body\'s "url" key is not a string');
|
|
||||||
}
|
|
||||||
let parsedUrl: URL;
|
let parsedUrl: URL;
|
||||||
try {
|
try {
|
||||||
parsedUrl = new URL(url);
|
parsedUrl = new URL(body.url);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
throw new Error("Token body's URL was not a valid URL");
|
throw new Error("Token body's URL was not a valid URL");
|
||||||
}
|
}
|
||||||
|
@ -38,7 +32,7 @@ const parseTokenBody = (
|
||||||
throw new Error("Token body's URL was not HTTPS");
|
throw new Error("Token body's URL was not HTTPS");
|
||||||
}
|
}
|
||||||
|
|
||||||
return { fields, url };
|
return body;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const uploadDebugLogs = async (
|
export const uploadDebugLogs = async (
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
// Copyright 2021 Signal Messenger, LLC
|
// Copyright 2021 Signal Messenger, LLC
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
import * as z from 'zod';
|
||||||
import * as pino from 'pino';
|
import * as pino from 'pino';
|
||||||
import { redactAll } from '../../js/modules/privacy';
|
import { redactAll } from '../../js/modules/privacy';
|
||||||
import { missingCaseError } from '../util/missingCaseError';
|
import { missingCaseError } from '../util/missingCaseError';
|
||||||
|
@ -19,37 +20,14 @@ export enum LogLevel {
|
||||||
|
|
||||||
// These match [Pino's core fields][1].
|
// These match [Pino's core fields][1].
|
||||||
// [1]: https://getpino.io/#/?id=usage
|
// [1]: https://getpino.io/#/?id=usage
|
||||||
export type LogEntryType = {
|
const logEntrySchema = z.object({
|
||||||
level: LogLevel;
|
level: z.nativeEnum(LogLevel),
|
||||||
msg: string;
|
msg: z.string(),
|
||||||
time: string;
|
time: z.string().refine(value => !Number.isNaN(new Date(value).getTime())),
|
||||||
};
|
});
|
||||||
|
export type LogEntryType = z.infer<typeof logEntrySchema>;
|
||||||
|
|
||||||
const logLevels = new Set<LogLevel>([
|
export const isLogEntry = logEntrySchema.check.bind(logEntrySchema);
|
||||||
LogLevel.Fatal,
|
|
||||||
LogLevel.Error,
|
|
||||||
LogLevel.Warn,
|
|
||||||
LogLevel.Info,
|
|
||||||
LogLevel.Debug,
|
|
||||||
LogLevel.Trace,
|
|
||||||
]);
|
|
||||||
function isLogLevel(value: unknown): value is LogLevel {
|
|
||||||
return typeof value === 'number' && logLevels.has(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
function isValidTime(value: unknown): value is string {
|
|
||||||
return typeof value === 'string' && !Number.isNaN(new Date(value).getTime());
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isLogEntry(value: unknown): value is LogEntryType {
|
|
||||||
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { level, time, msg } = value as Record<string, unknown>;
|
|
||||||
|
|
||||||
return typeof msg === 'string' && isLogLevel(level) && isValidTime(time);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getLogLevelString(value: LogLevel): pino.Level {
|
export function getLogLevelString(value: LogLevel): pino.Level {
|
||||||
switch (value) {
|
switch (value) {
|
||||||
|
|
|
@ -14802,7 +14802,7 @@
|
||||||
"rule": "jQuery-append(",
|
"rule": "jQuery-append(",
|
||||||
"path": "ts/logging/debuglogs.js",
|
"path": "ts/logging/debuglogs.js",
|
||||||
"line": " form.append('key', fields.key);",
|
"line": " form.append('key', fields.key);",
|
||||||
"lineNumber": 45,
|
"lineNumber": 61,
|
||||||
"reasonCategory": "falseMatch",
|
"reasonCategory": "falseMatch",
|
||||||
"updated": "2020-12-17T18:08:07.752Z"
|
"updated": "2020-12-17T18:08:07.752Z"
|
||||||
},
|
},
|
||||||
|
@ -14810,7 +14810,7 @@
|
||||||
"rule": "jQuery-append(",
|
"rule": "jQuery-append(",
|
||||||
"path": "ts/logging/debuglogs.js",
|
"path": "ts/logging/debuglogs.js",
|
||||||
"line": " form.append(key, value);",
|
"line": " form.append(key, value);",
|
||||||
"lineNumber": 49,
|
"lineNumber": 65,
|
||||||
"reasonCategory": "falseMatch",
|
"reasonCategory": "falseMatch",
|
||||||
"updated": "2020-12-17T18:08:07.752Z"
|
"updated": "2020-12-17T18:08:07.752Z"
|
||||||
},
|
},
|
||||||
|
@ -14818,7 +14818,7 @@
|
||||||
"rule": "jQuery-append(",
|
"rule": "jQuery-append(",
|
||||||
"path": "ts/logging/debuglogs.js",
|
"path": "ts/logging/debuglogs.js",
|
||||||
"line": " form.append('Content-Type', contentType);",
|
"line": " form.append('Content-Type', contentType);",
|
||||||
"lineNumber": 53,
|
"lineNumber": 69,
|
||||||
"reasonCategory": "falseMatch",
|
"reasonCategory": "falseMatch",
|
||||||
"updated": "2020-12-17T18:08:07.752Z"
|
"updated": "2020-12-17T18:08:07.752Z"
|
||||||
},
|
},
|
||||||
|
@ -14826,7 +14826,7 @@
|
||||||
"rule": "jQuery-append(",
|
"rule": "jQuery-append(",
|
||||||
"path": "ts/logging/debuglogs.js",
|
"path": "ts/logging/debuglogs.js",
|
||||||
"line": " form.append('file', contentBuffer, {",
|
"line": " form.append('file', contentBuffer, {",
|
||||||
"lineNumber": 54,
|
"lineNumber": 70,
|
||||||
"reasonCategory": "falseMatch",
|
"reasonCategory": "falseMatch",
|
||||||
"updated": "2020-12-17T18:08:07.752Z"
|
"updated": "2020-12-17T18:08:07.752Z"
|
||||||
},
|
},
|
||||||
|
@ -14834,7 +14834,7 @@
|
||||||
"rule": "jQuery-append(",
|
"rule": "jQuery-append(",
|
||||||
"path": "ts/logging/debuglogs.ts",
|
"path": "ts/logging/debuglogs.ts",
|
||||||
"line": " form.append('key', fields.key);",
|
"line": " form.append('key', fields.key);",
|
||||||
"lineNumber": 55,
|
"lineNumber": 49,
|
||||||
"reasonCategory": "falseMatch",
|
"reasonCategory": "falseMatch",
|
||||||
"updated": "2020-12-17T18:08:07.752Z"
|
"updated": "2020-12-17T18:08:07.752Z"
|
||||||
},
|
},
|
||||||
|
@ -14842,7 +14842,7 @@
|
||||||
"rule": "jQuery-append(",
|
"rule": "jQuery-append(",
|
||||||
"path": "ts/logging/debuglogs.ts",
|
"path": "ts/logging/debuglogs.ts",
|
||||||
"line": " form.append(key, value);",
|
"line": " form.append(key, value);",
|
||||||
"lineNumber": 59,
|
"lineNumber": 53,
|
||||||
"reasonCategory": "falseMatch",
|
"reasonCategory": "falseMatch",
|
||||||
"updated": "2020-12-17T18:08:07.752Z"
|
"updated": "2020-12-17T18:08:07.752Z"
|
||||||
},
|
},
|
||||||
|
@ -14850,7 +14850,7 @@
|
||||||
"rule": "jQuery-append(",
|
"rule": "jQuery-append(",
|
||||||
"path": "ts/logging/debuglogs.ts",
|
"path": "ts/logging/debuglogs.ts",
|
||||||
"line": " form.append('Content-Type', contentType);",
|
"line": " form.append('Content-Type', contentType);",
|
||||||
"lineNumber": 64,
|
"lineNumber": 58,
|
||||||
"reasonCategory": "falseMatch",
|
"reasonCategory": "falseMatch",
|
||||||
"updated": "2020-12-17T18:08:07.752Z"
|
"updated": "2020-12-17T18:08:07.752Z"
|
||||||
},
|
},
|
||||||
|
@ -14858,7 +14858,7 @@
|
||||||
"rule": "jQuery-append(",
|
"rule": "jQuery-append(",
|
||||||
"path": "ts/logging/debuglogs.ts",
|
"path": "ts/logging/debuglogs.ts",
|
||||||
"line": " form.append('file', contentBuffer, {",
|
"line": " form.append('file', contentBuffer, {",
|
||||||
"lineNumber": 65,
|
"lineNumber": 59,
|
||||||
"reasonCategory": "falseMatch",
|
"reasonCategory": "falseMatch",
|
||||||
"updated": "2020-12-17T18:08:07.752Z"
|
"updated": "2020-12-17T18:08:07.752Z"
|
||||||
},
|
},
|
||||||
|
|
|
@ -17620,3 +17620,8 @@ zip-stream@^1.2.0:
|
||||||
ffi-napi "2.4.5"
|
ffi-napi "2.4.5"
|
||||||
ref-array-napi "1.2.0"
|
ref-array-napi "1.2.0"
|
||||||
ref-napi "1.4.2"
|
ref-napi "1.4.2"
|
||||||
|
|
||||||
|
zod@1.11.13:
|
||||||
|
version "1.11.13"
|
||||||
|
resolved "https://registry.yarnpkg.com/zod/-/zod-1.11.13.tgz#6acb1e52b670afeb816ce2e2ddf6ab359f9ea506"
|
||||||
|
integrity sha512-10+KA7eWa8g1hbKIXkOnhjJ4RKEwX85ECz3VJzP+pWkJOFKn76bHy1kG0d1JHBwmdElLcCsaB0O9HqIfT1vZnw==
|
||||||
|
|
Loading…
Reference in a new issue