From 50f5af35941573ca4a0df9caaf5c84eadf8994a8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 7 Apr 2026 22:56:28 +0000 Subject: [PATCH 1/4] Add Object.create(null) exemption to no-null rule and tests Agent-Logs-Url: https://github.com/microsoft/rushstack/sessions/88c04cb4-f442-4fe2-8991-6a20aa3c6f9b Co-authored-by: dmichon-msft <26827560+dmichon-msft@users.noreply.github.com> --- eslint/eslint-plugin/src/no-null.ts | 25 ++++-- eslint/eslint-plugin/src/test/no-null.test.ts | 84 +++++++++++++++++++ 2 files changed, 104 insertions(+), 5 deletions(-) create mode 100644 eslint/eslint-plugin/src/test/no-null.test.ts diff --git a/eslint/eslint-plugin/src/no-null.ts b/eslint/eslint-plugin/src/no-null.ts index 3525d0128a4..94e980bfb59 100644 --- a/eslint/eslint-plugin/src/no-null.ts +++ b/eslint/eslint-plugin/src/no-null.ts @@ -2,6 +2,7 @@ // See LICENSE in the project root for license information. import type { TSESTree, TSESLint } from '@typescript-eslint/utils'; +import { AST_NODE_TYPES } from '@typescript-eslint/utils'; type MessageIds = 'error-usage-of-null'; type Options = []; @@ -27,15 +28,29 @@ const noNullRule: TSESLint.RuleModule = { // Is it a "null" literal? if (node.value === null) { // Does the "null" appear in a comparison such as "if (x === null)"? - let isComparison: boolean = false; - if (node.parent && node.parent.type === 'BinaryExpression') { + if (node.parent && node.parent.type === AST_NODE_TYPES.BinaryExpression) { const operator: string = node.parent.operator; - isComparison = operator === '!==' || operator === '===' || operator === '!=' || operator === '=='; + if (operator === '!==' || operator === '===' || operator === '!=' || operator === '==') { + return; + } } - if (!isComparison) { - context.report({ node, messageId: 'error-usage-of-null' }); + // Is this "Object.create(null)"? This is the correct pattern for creating + // a dictionary object that does not inherit members from the Object prototype. + if ( + node.parent && + node.parent.type === AST_NODE_TYPES.CallExpression && + node.parent.arguments[0] === node && + node.parent.callee.type === AST_NODE_TYPES.MemberExpression && + node.parent.callee.object.type === AST_NODE_TYPES.Identifier && + node.parent.callee.object.name === 'Object' && + node.parent.callee.property.type === AST_NODE_TYPES.Identifier && + node.parent.callee.property.name === 'create' + ) { + return; } + + context.report({ node, messageId: 'error-usage-of-null' }); } } }; diff --git a/eslint/eslint-plugin/src/test/no-null.test.ts b/eslint/eslint-plugin/src/test/no-null.test.ts new file mode 100644 index 00000000000..a5b65697bcd --- /dev/null +++ b/eslint/eslint-plugin/src/test/no-null.test.ts @@ -0,0 +1,84 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import type { RuleTester } from '@typescript-eslint/rule-tester'; + +import { getRuleTesterWithoutProject } from './ruleTester'; +import { noNullRule } from '../no-null'; + +const ruleTester: RuleTester = getRuleTesterWithoutProject(); + +ruleTester.run('no-null', noNullRule, { + invalid: [ + { + // Assigning null to a variable + code: 'let x = null;', + errors: [{ messageId: 'error-usage-of-null' }] + }, + { + // Passing null as a function argument (not Object.create) + code: 'foo(null);', + errors: [{ messageId: 'error-usage-of-null' }] + }, + { + // Returning null + code: 'function f() { return null; }', + errors: [{ messageId: 'error-usage-of-null' }] + }, + { + // Using null in a ternary + code: 'let x = true ? null : undefined;', + errors: [{ messageId: 'error-usage-of-null' }] + }, + { + // Using null as a second argument to Object.create + code: 'Object.create(proto, null);', + errors: [{ messageId: 'error-usage-of-null' }] + }, + { + // Using null on a different method of Object + code: 'Object.assign(null);', + errors: [{ messageId: 'error-usage-of-null' }] + }, + { + // Using null with a different object's create method + code: 'NotObject.create(null);', + errors: [{ messageId: 'error-usage-of-null' }] + }, + { + // Computed property access: Object["create"](null) is NOT exempted + code: 'Object["create"](null);', + errors: [{ messageId: 'error-usage-of-null' }] + } + ], + valid: [ + { + // Comparison with === is allowed + code: 'if (x === null) {}' + }, + { + // Comparison with !== is allowed + code: 'if (x !== null) {}' + }, + { + // Comparison with == is allowed + code: 'if (x == null) {}' + }, + { + // Comparison with != is allowed + code: 'if (x != null) {}' + }, + { + // Object.create(null) is allowed for creating prototype-less dictionary objects + code: 'const dict = Object.create(null);' + }, + { + // Object.create(null) with type annotation + code: 'const dict: Record = Object.create(null);' + }, + { + // Object.create(null) inside a function + code: 'function createDict() { return Object.create(null); }' + } + ] +}); From 602a68b8a4075fe1879fce4cee6ec77ddb2f2aad Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 7 Apr 2026 22:57:44 +0000 Subject: [PATCH 2/4] Add null-prototype-dictionaries rule enforcing Object.create(null) for Record types Agent-Logs-Url: https://github.com/microsoft/rushstack/sessions/88c04cb4-f442-4fe2-8991-6a20aa3c6f9b Co-authored-by: dmichon-msft <26827560+dmichon-msft@users.noreply.github.com> --- eslint/eslint-plugin/src/index.ts | 4 + .../src/null-prototype-dictionaries.ts | 90 +++++++++++++++++++ .../test/null-prototype-dictionaries.test.ts | 74 +++++++++++++++ 3 files changed, 168 insertions(+) create mode 100644 eslint/eslint-plugin/src/null-prototype-dictionaries.ts create mode 100644 eslint/eslint-plugin/src/test/null-prototype-dictionaries.test.ts diff --git a/eslint/eslint-plugin/src/index.ts b/eslint/eslint-plugin/src/index.ts index 61f0c64f23e..4e1cd4729ab 100644 --- a/eslint/eslint-plugin/src/index.ts +++ b/eslint/eslint-plugin/src/index.ts @@ -8,6 +8,7 @@ import { noBackslashImportsRule } from './no-backslash-imports'; import { noExternalLocalImportsRule } from './no-external-local-imports'; import { noNewNullRule } from './no-new-null'; import { noNullRule } from './no-null'; +import { nullPrototypeDictionariesRule } from './null-prototype-dictionaries'; import { noTransitiveDependencyImportsRule } from './no-transitive-dependency-imports'; import { noUntypedUnderscoreRule } from './no-untyped-underscore'; import { normalizedImportsRule } from './normalized-imports'; @@ -36,6 +37,9 @@ const plugin: IPlugin = { // Full name: "@rushstack/no-null" 'no-null': noNullRule, + // Full name: "@rushstack/null-prototype-dictionaries" + 'null-prototype-dictionaries': nullPrototypeDictionariesRule, + // Full name: "@rushstack/no-transitive-dependency-imports" 'no-transitive-dependency-imports': noTransitiveDependencyImportsRule, diff --git a/eslint/eslint-plugin/src/null-prototype-dictionaries.ts b/eslint/eslint-plugin/src/null-prototype-dictionaries.ts new file mode 100644 index 00000000000..3fa36867b93 --- /dev/null +++ b/eslint/eslint-plugin/src/null-prototype-dictionaries.ts @@ -0,0 +1,90 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import type { TSESTree, TSESLint, ParserServices } from '@typescript-eslint/utils'; +import type * as ts from 'typescript'; + +type MessageIds = 'error-object-literal-dictionary'; +type Options = []; + +const nullPrototypeDictionariesRule: TSESLint.RuleModule = { + defaultOptions: [], + meta: { + type: 'problem', + messages: { + 'error-object-literal-dictionary': + 'Dictionary objects typed as Record should be created using Object.create(null)' + + ' instead of an object literal. This avoids prototype pollution, collisions with' + + ' Object.prototype members such as "toString", and enables higher performance since runtimes' + + ' such as V8 process Object.create(null) as opting out of having a hidden class and going' + + ' directly to dictionary mode.' + }, + schema: [], + docs: { + description: + 'Enforce that objects typed as string-keyed dictionaries (Record) are instantiated' + + ' using Object.create(null) instead of object literals, to avoid prototype pollution issues,' + + ' collisions with Object.prototype members such as "toString", and for higher performance' + + ' since runtimes such as V8 process Object.create(null) as opting out of having a hidden' + + ' class and going directly to dictionary mode', + recommended: 'strict', + url: 'https://www.npmjs.com/package/@rushstack/eslint-plugin' + } as TSESLint.RuleMetaDataDocs + }, + create: (context: TSESLint.RuleContext) => { + const parserServices: Partial | undefined = + context.sourceCode?.parserServices ?? context.parserServices; + if (!parserServices || !parserServices.program || !parserServices.esTreeNodeToTSNodeMap) { + throw new Error( + 'This rule requires your ESLint configuration to define the "parserOptions.project"' + + ' property for "@typescript-eslint/parser".' + ); + } + + const typeChecker: ts.TypeChecker = parserServices.program.getTypeChecker(); + + /** + * Checks whether the given type represents a pure string-keyed dictionary type: + * it has a string index signature and no named properties. + */ + function isStringKeyedDictionaryType(type: ts.Type): boolean { + // Check if the type has a string index signature + if (!type.getStringIndexType()) { + return false; + } + + // A pure dictionary type has no named properties - only an index signature. + // Types with named properties (like interfaces with extra index signatures) + // are not considered pure dictionaries. + if (type.getProperties().length > 0) { + return false; + } + + return true; + } + + return { + ObjectExpression(node: TSESTree.ObjectExpression): void { + const tsNode: ts.Node = parserServices.esTreeNodeToTSNodeMap!.get(node); + + // Get the contextual type (the type expected by the surrounding context, + // e.g. from a variable declaration's type annotation) + const contextualType: ts.Type | undefined = typeChecker.getContextualType( + tsNode as ts.Expression + ); + if (!contextualType) { + return; + } + + if (isStringKeyedDictionaryType(contextualType)) { + context.report({ + node, + messageId: 'error-object-literal-dictionary' + }); + } + } + }; + } +}; + +export { nullPrototypeDictionariesRule }; diff --git a/eslint/eslint-plugin/src/test/null-prototype-dictionaries.test.ts b/eslint/eslint-plugin/src/test/null-prototype-dictionaries.test.ts new file mode 100644 index 00000000000..a99afea89c4 --- /dev/null +++ b/eslint/eslint-plugin/src/test/null-prototype-dictionaries.test.ts @@ -0,0 +1,74 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import type { RuleTester } from '@typescript-eslint/rule-tester'; + +import { getRuleTesterWithProject } from './ruleTester'; +import { nullPrototypeDictionariesRule } from '../null-prototype-dictionaries'; + +const ruleTester: RuleTester = getRuleTesterWithProject(); + +ruleTester.run('null-prototype-dictionaries', nullPrototypeDictionariesRule, { + invalid: [ + { + // Empty object literal assigned to Record + code: 'const dict: Record = {};', + errors: [{ messageId: 'error-object-literal-dictionary' }] + }, + { + // Empty object literal assigned to index signature type + code: 'const dict: { [key: string]: number } = {};', + errors: [{ messageId: 'error-object-literal-dictionary' }] + }, + { + // Non-empty object literal assigned to Record type + code: 'const dict: Record = { a: "hello" };', + errors: [{ messageId: 'error-object-literal-dictionary' }] + }, + { + // Reassignment to empty object literal + code: [ + 'let dict: Record;', + 'dict = {};' + ].join('\n'), + errors: [{ messageId: 'error-object-literal-dictionary' }] + }, + { + // Return value from function + code: 'function f(): Record { return {}; }', + errors: [{ messageId: 'error-object-literal-dictionary' }] + } + ], + valid: [ + { + // Correct pattern: Object.create(null) for dictionary + code: 'const dict: Record = Object.create(null);' + }, + { + // Regular object type with named properties (not a dictionary) + code: 'const obj: { name: string } = { name: "hello" };' + }, + { + // No explicit dictionary type annotation + code: 'const obj = {};' + }, + { + // Record with literal union key type resolves to named properties, not a dictionary + code: 'const obj: Record<"a" | "b", number> = { a: 1, b: 2 };' + }, + { + // Interface with named properties AND index signature is not a pure dictionary + code: [ + 'interface IExtended { name: string; [key: string]: string }', + 'const obj: IExtended = { name: "hello" };' + ].join('\n') + }, + { + // Non-object-literal initializer is fine + code: [ + 'function getDict(): Record { return Object.create(null); }', + 'const dict: Record = getDict();' + ].join('\n') + } + ] +}); From 3650733623bbd9e6fcacdf78b09fab528bbcc1c4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 8 Apr 2026 21:12:59 +0000 Subject: [PATCH 3/4] Require __proto__: null for non-empty dictionary literals, Object.create(null) for empty ones Agent-Logs-Url: https://github.com/microsoft/rushstack/sessions/39bd15e2-0089-46b6-99ac-4bd914e5ba0a Co-authored-by: dmichon-msft <26827560+dmichon-msft@users.noreply.github.com> --- .../src/null-prototype-dictionaries.ts | 39 ++++++++++++++++--- .../test/null-prototype-dictionaries.test.ts | 29 +++++++++----- 2 files changed, 52 insertions(+), 16 deletions(-) diff --git a/eslint/eslint-plugin/src/null-prototype-dictionaries.ts b/eslint/eslint-plugin/src/null-prototype-dictionaries.ts index 3fa36867b93..9693084c7ef 100644 --- a/eslint/eslint-plugin/src/null-prototype-dictionaries.ts +++ b/eslint/eslint-plugin/src/null-prototype-dictionaries.ts @@ -4,7 +4,7 @@ import type { TSESTree, TSESLint, ParserServices } from '@typescript-eslint/utils'; import type * as ts from 'typescript'; -type MessageIds = 'error-object-literal-dictionary'; +type MessageIds = 'error-empty-object-literal-dictionary' | 'error-missing-null-prototype'; type Options = []; const nullPrototypeDictionariesRule: TSESLint.RuleModule = { @@ -12,12 +12,15 @@ const nullPrototypeDictionariesRule: TSESLint.RuleModule = meta: { type: 'problem', messages: { - 'error-object-literal-dictionary': + 'error-empty-object-literal-dictionary': 'Dictionary objects typed as Record should be created using Object.create(null)' + - ' instead of an object literal. This avoids prototype pollution, collisions with' + + ' instead of an empty object literal. This avoids prototype pollution, collisions with' + ' Object.prototype members such as "toString", and enables higher performance since runtimes' + ' such as V8 process Object.create(null) as opting out of having a hidden class and going' + - ' directly to dictionary mode.' + ' directly to dictionary mode.', + 'error-missing-null-prototype': + 'Dictionary object literals typed as Record should include "__proto__: null"' + + ' to avoid prototype pollution and collisions with Object.prototype members such as "toString".' }, schema: [], docs: { @@ -76,10 +79,34 @@ const nullPrototypeDictionariesRule: TSESLint.RuleModule = return; } - if (isStringKeyedDictionaryType(contextualType)) { + if (!isStringKeyedDictionaryType(contextualType)) { + return; + } + + // For empty object literals, recommend Object.create(null) which is more performant + if (node.properties.length === 0) { + context.report({ + node, + messageId: 'error-empty-object-literal-dictionary' + }); + return; + } + + // For non-empty object literals, check whether "__proto__: null" is present + const hasNullProto: boolean = node.properties.some( + (prop) => + prop.type === 'Property' && + !prop.computed && + ((prop.key.type === 'Identifier' && prop.key.name === '__proto__') || + (prop.key.type === 'Literal' && prop.key.value === '__proto__')) && + prop.value.type === 'Literal' && + prop.value.value === null + ); + + if (!hasNullProto) { context.report({ node, - messageId: 'error-object-literal-dictionary' + messageId: 'error-missing-null-prototype' }); } } diff --git a/eslint/eslint-plugin/src/test/null-prototype-dictionaries.test.ts b/eslint/eslint-plugin/src/test/null-prototype-dictionaries.test.ts index a99afea89c4..9e26fbb2cfe 100644 --- a/eslint/eslint-plugin/src/test/null-prototype-dictionaries.test.ts +++ b/eslint/eslint-plugin/src/test/null-prototype-dictionaries.test.ts @@ -13,17 +13,12 @@ ruleTester.run('null-prototype-dictionaries', nullPrototypeDictionariesRule, { { // Empty object literal assigned to Record code: 'const dict: Record = {};', - errors: [{ messageId: 'error-object-literal-dictionary' }] + errors: [{ messageId: 'error-empty-object-literal-dictionary' }] }, { // Empty object literal assigned to index signature type code: 'const dict: { [key: string]: number } = {};', - errors: [{ messageId: 'error-object-literal-dictionary' }] - }, - { - // Non-empty object literal assigned to Record type - code: 'const dict: Record = { a: "hello" };', - errors: [{ messageId: 'error-object-literal-dictionary' }] + errors: [{ messageId: 'error-empty-object-literal-dictionary' }] }, { // Reassignment to empty object literal @@ -31,19 +26,33 @@ ruleTester.run('null-prototype-dictionaries', nullPrototypeDictionariesRule, { 'let dict: Record;', 'dict = {};' ].join('\n'), - errors: [{ messageId: 'error-object-literal-dictionary' }] + errors: [{ messageId: 'error-empty-object-literal-dictionary' }] }, { // Return value from function code: 'function f(): Record { return {}; }', - errors: [{ messageId: 'error-object-literal-dictionary' }] + errors: [{ messageId: 'error-empty-object-literal-dictionary' }] + }, + { + // Non-empty object literal without __proto__: null + code: 'const dict: Record = { a: "hello" };', + errors: [{ messageId: 'error-missing-null-prototype' }] + }, + { + // Non-empty object literal with __proto__ set to something other than null + code: 'const dict: Record = { __proto__: Object.prototype, a: "hello" };', + errors: [{ messageId: 'error-missing-null-prototype' }] } ], valid: [ { - // Correct pattern: Object.create(null) for dictionary + // Correct pattern: Object.create(null) for empty dictionary code: 'const dict: Record = Object.create(null);' }, + { + // Correct pattern: non-empty literal with __proto__: null + code: 'const dict: Record = { __proto__: null, a: "hello" };' + }, { // Regular object type with named properties (not a dictionary) code: 'const obj: { name: string } = { name: "hello" };' From d92c8104e8cde8c4f90692b9beec2b98094592e9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 8 Apr 2026 21:28:25 +0000 Subject: [PATCH 4/4] Only flag empty {} for null-prototype-dictionaries; allow __proto__: null in no-null Agent-Logs-Url: https://github.com/microsoft/rushstack/sessions/1d063584-7336-4ab7-b4d0-e2a46bebba49 Co-authored-by: dmichon-msft <26827560+dmichon-msft@users.noreply.github.com> --- eslint/eslint-plugin/src/no-null.ts | 14 ++++++++++ .../src/null-prototype-dictionaries.ts | 28 ++----------------- eslint/eslint-plugin/src/test/no-null.test.ts | 13 +++++++++ .../test/null-prototype-dictionaries.test.ts | 16 ++++------- 4 files changed, 35 insertions(+), 36 deletions(-) diff --git a/eslint/eslint-plugin/src/no-null.ts b/eslint/eslint-plugin/src/no-null.ts index 94e980bfb59..acb531cfbca 100644 --- a/eslint/eslint-plugin/src/no-null.ts +++ b/eslint/eslint-plugin/src/no-null.ts @@ -50,6 +50,20 @@ const noNullRule: TSESLint.RuleModule = { return; } + // Is this "__proto__: null" inside an object literal? This is used to create + // an object literal that does not inherit from Object.prototype. + if ( + node.parent && + node.parent.type === AST_NODE_TYPES.Property && + !node.parent.computed && + ((node.parent.key.type === AST_NODE_TYPES.Identifier && + node.parent.key.name === '__proto__') || + (node.parent.key.type === AST_NODE_TYPES.Literal && + node.parent.key.value === '__proto__')) + ) { + return; + } + context.report({ node, messageId: 'error-usage-of-null' }); } } diff --git a/eslint/eslint-plugin/src/null-prototype-dictionaries.ts b/eslint/eslint-plugin/src/null-prototype-dictionaries.ts index 9693084c7ef..6e8e9594206 100644 --- a/eslint/eslint-plugin/src/null-prototype-dictionaries.ts +++ b/eslint/eslint-plugin/src/null-prototype-dictionaries.ts @@ -4,7 +4,7 @@ import type { TSESTree, TSESLint, ParserServices } from '@typescript-eslint/utils'; import type * as ts from 'typescript'; -type MessageIds = 'error-empty-object-literal-dictionary' | 'error-missing-null-prototype'; +type MessageIds = 'error-empty-object-literal-dictionary'; type Options = []; const nullPrototypeDictionariesRule: TSESLint.RuleModule = { @@ -17,10 +17,7 @@ const nullPrototypeDictionariesRule: TSESLint.RuleModule = ' instead of an empty object literal. This avoids prototype pollution, collisions with' + ' Object.prototype members such as "toString", and enables higher performance since runtimes' + ' such as V8 process Object.create(null) as opting out of having a hidden class and going' + - ' directly to dictionary mode.', - 'error-missing-null-prototype': - 'Dictionary object literals typed as Record should include "__proto__: null"' + - ' to avoid prototype pollution and collisions with Object.prototype members such as "toString".' + ' directly to dictionary mode.' }, schema: [], docs: { @@ -83,31 +80,12 @@ const nullPrototypeDictionariesRule: TSESLint.RuleModule = return; } - // For empty object literals, recommend Object.create(null) which is more performant + // Only flag empty object literals; non-empty literals are allowed for now if (node.properties.length === 0) { context.report({ node, messageId: 'error-empty-object-literal-dictionary' }); - return; - } - - // For non-empty object literals, check whether "__proto__: null" is present - const hasNullProto: boolean = node.properties.some( - (prop) => - prop.type === 'Property' && - !prop.computed && - ((prop.key.type === 'Identifier' && prop.key.name === '__proto__') || - (prop.key.type === 'Literal' && prop.key.value === '__proto__')) && - prop.value.type === 'Literal' && - prop.value.value === null - ); - - if (!hasNullProto) { - context.report({ - node, - messageId: 'error-missing-null-prototype' - }); } } }; diff --git a/eslint/eslint-plugin/src/test/no-null.test.ts b/eslint/eslint-plugin/src/test/no-null.test.ts index a5b65697bcd..61f0963dbee 100644 --- a/eslint/eslint-plugin/src/test/no-null.test.ts +++ b/eslint/eslint-plugin/src/test/no-null.test.ts @@ -49,6 +49,11 @@ ruleTester.run('no-null', noNullRule, { // Computed property access: Object["create"](null) is NOT exempted code: 'Object["create"](null);', errors: [{ messageId: 'error-usage-of-null' }] + }, + { + // null on a non-__proto__ property is still flagged + code: 'const x = { foo: null };', + errors: [{ messageId: 'error-usage-of-null' }] } ], valid: [ @@ -79,6 +84,14 @@ ruleTester.run('no-null', noNullRule, { { // Object.create(null) inside a function code: 'function createDict() { return Object.create(null); }' + }, + { + // __proto__: null is allowed in object literals + code: 'const obj = { __proto__: null, a: 1 };' + }, + { + // __proto__: null as string key is also allowed + code: 'const obj = { "__proto__": null, a: 1 };' } ] }); diff --git a/eslint/eslint-plugin/src/test/null-prototype-dictionaries.test.ts b/eslint/eslint-plugin/src/test/null-prototype-dictionaries.test.ts index 9e26fbb2cfe..45e14b09c21 100644 --- a/eslint/eslint-plugin/src/test/null-prototype-dictionaries.test.ts +++ b/eslint/eslint-plugin/src/test/null-prototype-dictionaries.test.ts @@ -32,16 +32,6 @@ ruleTester.run('null-prototype-dictionaries', nullPrototypeDictionariesRule, { // Return value from function code: 'function f(): Record { return {}; }', errors: [{ messageId: 'error-empty-object-literal-dictionary' }] - }, - { - // Non-empty object literal without __proto__: null - code: 'const dict: Record = { a: "hello" };', - errors: [{ messageId: 'error-missing-null-prototype' }] - }, - { - // Non-empty object literal with __proto__ set to something other than null - code: 'const dict: Record = { __proto__: Object.prototype, a: "hello" };', - errors: [{ messageId: 'error-missing-null-prototype' }] } ], valid: [ @@ -50,7 +40,11 @@ ruleTester.run('null-prototype-dictionaries', nullPrototypeDictionariesRule, { code: 'const dict: Record = Object.create(null);' }, { - // Correct pattern: non-empty literal with __proto__: null + // Non-empty object literal is allowed for now + code: 'const dict: Record = { a: "hello" };' + }, + { + // Non-empty literal with __proto__: null is also fine code: 'const dict: Record = { __proto__: null, a: "hello" };' }, {