fix(cli/rt): make some web API constructors illegal at runtime (#7468)

This commit is contained in:
Nayeem Rahman 2020-09-19 22:30:59 +01:00 committed by GitHub
parent 79e5b57663
commit aaa5e6613a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 106 additions and 15 deletions

View file

@ -1113,7 +1113,7 @@ declare namespace Deno {
/** see: https://w3c.github.io/permissions/#permissionstatus */ /** see: https://w3c.github.io/permissions/#permissionstatus */
export class PermissionStatus { export class PermissionStatus {
state: PermissionState; state: PermissionState;
constructor(state: PermissionState); constructor();
} }
/** **UNSTABLE**: New API, yet to be vetted. Additional consideration is still /** **UNSTABLE**: New API, yet to be vetted. Additional consideration is still

View file

@ -1,6 +1,8 @@
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
((window) => { ((window) => {
const illegalConstructorKey = Symbol("illegalConstructorKey");
function isInvalidDate(x) { function isInvalidDate(x) {
return isNaN(x.getTime()); return isNaN(x.getTime());
} }
@ -146,6 +148,7 @@
} }
window.__bootstrap.webUtil = { window.__bootstrap.webUtil = {
illegalConstructorKey,
isInvalidDate, isInvalidDate,
requiredArguments, requiredArguments,
immutableDefine, immutableDefine,

View file

@ -2,7 +2,7 @@
((window) => { ((window) => {
const { opNow } = window.__bootstrap.timers; const { opNow } = window.__bootstrap.timers;
const { cloneValue } = window.__bootstrap.webUtil; const { cloneValue, illegalConstructorKey } = window.__bootstrap.webUtil;
const customInspect = Symbol.for("Deno.customInspect"); const customInspect = Symbol.for("Deno.customInspect");
let performanceEntries = []; let performanceEntries = [];
@ -74,7 +74,11 @@
entryType, entryType,
startTime, startTime,
duration, duration,
key,
) { ) {
if (key != illegalConstructorKey) {
throw new TypeError("Illegal constructor.");
}
this.#name = name; this.#name = name;
this.#entryType = entryType; this.#entryType = entryType;
this.#startTime = startTime; this.#startTime = startTime;
@ -110,7 +114,7 @@
name, name,
{ detail = null, startTime = now() } = {}, { detail = null, startTime = now() } = {},
) { ) {
super(name, "mark", startTime, 0); super(name, "mark", startTime, 0, illegalConstructorKey);
if (startTime < 0) { if (startTime < 0) {
throw new TypeError("startTime cannot be negative"); throw new TypeError("startTime cannot be negative");
} }
@ -152,8 +156,12 @@
startTime, startTime,
duration, duration,
detail = null, detail = null,
key,
) { ) {
super(name, "measure", startTime, duration); if (key != illegalConstructorKey) {
throw new TypeError("Illegal constructor.");
}
super(name, "measure", startTime, duration, illegalConstructorKey);
this.#detail = cloneValue(detail); this.#detail = cloneValue(detail);
} }
@ -177,6 +185,12 @@
} }
class Performance { class Performance {
constructor(key) {
if (key != illegalConstructorKey) {
throw new TypeError("Illegal constructor.");
}
}
clearMarks(markName) { clearMarks(markName) {
if (markName == null) { if (markName == null) {
performanceEntries = performanceEntries.filter( performanceEntries = performanceEntries.filter(
@ -302,6 +316,7 @@
typeof startOrMeasureOptions === "object" typeof startOrMeasureOptions === "object"
? startOrMeasureOptions.detail ?? null ? startOrMeasureOptions.detail ?? null
: null, : null,
illegalConstructorKey,
); );
performanceEntries.push(entry); performanceEntries.push(entry);
return entry; return entry;
@ -312,10 +327,13 @@
} }
} }
const performance = new Performance(illegalConstructorKey);
window.__bootstrap.performance = { window.__bootstrap.performance = {
PerformanceEntry, PerformanceEntry,
PerformanceMark, PerformanceMark,
PerformanceMeasure, PerformanceMeasure,
Performance, Performance,
performance,
}; };
})(this); })(this);

View file

@ -2,6 +2,7 @@
((window) => { ((window) => {
const core = window.Deno.core; const core = window.Deno.core;
const { illegalConstructorKey } = window.__bootstrap.webUtil;
function opQuery(desc) { function opQuery(desc) {
return core.jsonOpSync("op_query_permission", desc).state; return core.jsonOpSync("op_query_permission", desc).state;
@ -16,30 +17,45 @@
} }
class PermissionStatus { class PermissionStatus {
constructor(state) { constructor(state, key) {
if (key != illegalConstructorKey) {
throw new TypeError("Illegal constructor.");
}
this.state = state; this.state = state;
} }
// TODO(kt3k): implement onchange handler // TODO(kt3k): implement onchange handler
} }
class Permissions { class Permissions {
constructor(key) {
if (key != illegalConstructorKey) {
throw new TypeError("Illegal constructor.");
}
}
query(desc) { query(desc) {
const state = opQuery(desc); const state = opQuery(desc);
return Promise.resolve(new PermissionStatus(state)); return Promise.resolve(
new PermissionStatus(state, illegalConstructorKey),
);
} }
revoke(desc) { revoke(desc) {
const state = opRevoke(desc); const state = opRevoke(desc);
return Promise.resolve(new PermissionStatus(state)); return Promise.resolve(
new PermissionStatus(state, illegalConstructorKey),
);
} }
request(desc) { request(desc) {
const state = opRequest(desc); const state = opRequest(desc);
return Promise.resolve(new PermissionStatus(state)); return Promise.resolve(
new PermissionStatus(state, illegalConstructorKey),
);
} }
} }
const permissions = new Permissions(); const permissions = new Permissions(illegalConstructorKey);
window.__bootstrap.permissions = { window.__bootstrap.permissions = {
permissions, permissions,

View file

@ -235,7 +235,7 @@ delete Object.prototype.__proto__;
crypto: util.readOnly(crypto), crypto: util.readOnly(crypto),
dispatchEvent: util.readOnly(EventTarget.prototype.dispatchEvent), dispatchEvent: util.readOnly(EventTarget.prototype.dispatchEvent),
fetch: util.writable(fetch.fetch), fetch: util.writable(fetch.fetch),
performance: util.writable(new performance.Performance()), performance: util.writable(performance.performance),
removeEventListener: util.readOnly( removeEventListener: util.readOnly(
EventTarget.prototype.removeEventListener, EventTarget.prototype.removeEventListener,
), ),

View file

@ -3,6 +3,7 @@ import {
unitTest, unitTest,
assert, assert,
assertEquals, assertEquals,
assertThrows,
createResolvable, createResolvable,
} from "./test_util.ts"; } from "./test_util.ts";
@ -64,3 +65,19 @@ unitTest(function performanceMeasure() {
}, 100); }, 100);
}); });
}); });
unitTest(function performanceIllegalConstructor() {
assertThrows(() => new Performance(), TypeError, "Illegal constructor.");
});
unitTest(function performanceEntryIllegalConstructor() {
assertThrows(() => new PerformanceEntry(), TypeError, "Illegal constructor.");
});
unitTest(function performanceMeasureIllegalConstructor() {
assertThrows(
() => new PerformanceMeasure(),
TypeError,
"Illegal constructor.",
);
});

View file

@ -1,5 +1,5 @@
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
import { unitTest, assertThrowsAsync } from "./test_util.ts"; import { unitTest, assertThrows, assertThrowsAsync } from "./test_util.ts";
unitTest(async function permissionInvalidName(): Promise<void> { unitTest(async function permissionInvalidName(): Promise<void> {
await assertThrowsAsync(async () => { await assertThrowsAsync(async () => {
@ -13,3 +13,15 @@ unitTest(async function permissionNetInvalidUrl(): Promise<void> {
await Deno.permissions.query({ name: "net", url: ":" }); await Deno.permissions.query({ name: "net", url: ":" });
}, URIError); }, URIError);
}); });
unitTest(function permissionsIllegalConstructor() {
assertThrows(() => new Deno.Permissions(), TypeError, "Illegal constructor.");
});
unitTest(function permissionStatusIllegalConstructor() {
assertThrows(
() => new Deno.PermissionStatus(),
TypeError,
"Illegal constructor.",
);
});

View file

@ -5,6 +5,8 @@
const signalAbort = Symbol("signalAbort"); const signalAbort = Symbol("signalAbort");
const remove = Symbol("remove"); const remove = Symbol("remove");
const illegalConstructorKey = Symbol("illegalConstructorKey");
class AbortSignal extends EventTarget { class AbortSignal extends EventTarget {
#aborted = false; #aborted = false;
#abortAlgorithms = new Set(); #abortAlgorithms = new Set();
@ -29,7 +31,10 @@
this.#abortAlgorithms.delete(algorithm); this.#abortAlgorithms.delete(algorithm);
} }
constructor() { constructor(key) {
if (key != illegalConstructorKey) {
throw new TypeError("Illegal constructor.");
}
super(); super();
this.onabort = null; this.onabort = null;
this.addEventListener("abort", (evt) => { this.addEventListener("abort", (evt) => {
@ -50,7 +55,7 @@
} }
class AbortController { class AbortController {
#signal = new AbortSignal(); #signal = new AbortSignal(illegalConstructorKey);
get signal() { get signal() {
return this.#signal; return this.#signal;

View file

@ -9,6 +9,19 @@ function assertEquals(left, right) {
assert(left === right); assert(left === right);
} }
function assertThrows(fn) {
let error = null;
try {
fn();
} catch (error_) {
error = error_;
}
if (error == null) {
throw new Error("Didn't throw.");
}
return error;
}
function basicAbortController() { function basicAbortController() {
controller = new AbortController(); controller = new AbortController();
assert(controller); assert(controller);
@ -64,12 +77,19 @@ function controllerHasProperToString() {
assertEquals(actual, "[object AbortController]"); assertEquals(actual, "[object AbortController]");
} }
function abortSignalIllegalConstructor() {
const error = assertThrows(() => new AbortSignal());
assert(error instanceof TypeError);
assertEquals(error.message, "Illegal constructor.");
}
function main() { function main() {
basicAbortController(); basicAbortController();
signalCallsOnabort(); signalCallsOnabort();
signalEventListener(); signalEventListener();
onlyAbortsOnce(); onlyAbortsOnce();
controllerHasProperToString(); controllerHasProperToString();
abortSignalIllegalConstructor();
} }
main(); main();