-
Notifications
You must be signed in to change notification settings - Fork 72
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Lint to disallow SES polymorphic calls #827
Conversation
here's one example. should we be concerned that this could be dangerous?
|
added a "preview" of the call to the lint message
|
ab9f4d3
to
cfc4709
Compare
@kumavis I did a rebase and force-pushed with extra commits to cut the warnings down by half. I’m shooting to have most of them addressed with another commit or so today. One of the interesting ones is using the compartment API itself as intrinsics, which will take some refactoring. |
@kumavis Added commits to eliminate the warnings. This is ready for broader review and I’ll look into rebasing and cleanup tomorrow. |
let propertyHint | ||
if (object.type === 'Identifier') { | ||
objectHint = object.name | ||
} else if (object.type === 'MemberExpression') { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does it cover a[b]
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This certainly did produce lint errors for a[b]()
syntax.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes same member expression but with computed flag set to true
It does catch that, indeed.
…On Sun, Jul 18, 2021 at 9:00 PM Jack Works ***@***.***> wrote:
***@***.**** commented on this pull request.
------------------------------
In packages/eslint-plugin/lib/rules/no-polymorphic-call.js
<#827 (comment)>:
> + return
+ }
+ const reportHint = prepareMemberExpressionHint(node.callee)
+ context.report(node, `Polymorphic call: "${reportHint}". May be vulnerable to corruption or trap`)
+ }
+ }
+ },
+}
+
+function prepareMemberExpressionHint (node) {
+ const { object, property, computed } = node
+ let objectHint
+ let propertyHint
+ if (object.type === 'Identifier') {
+ objectHint = object.name
+ } else if (object.type === 'MemberExpression') {
Does it cover a[b]?
—
You are receiving this because you were assigned.
Reply to this email directly, view it on GitHub
<#827 (review)>, or
unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAAOXBQ7Q2LPQSZKT2VWFXLTYOPNZANCNFSM5ADJ3JBQ>
.
|
c58c520
to
82c38bf
Compare
@@ -0,0 +1,72 @@ | |||
/// <reference types="ses"> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The Compartment.evaluate
implementation needed to be extracted into a stand-alone module so it could be used from both compartment-shim.js
and module-instance.js
without dynamic dispatch. The internals communicate purely through their captured private fields.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I (MarkM) did not review this refactoring.
baseConsole[name](...args); | ||
} | ||
}; | ||
return [name, freeze(method)]; | ||
}); | ||
const filteringConsole = fromEntries(methods); | ||
return freeze(filteringConsole); | ||
return /** @type {VirtualConsole} */ (freeze(filteringConsole)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is an unrelated change that was necessary to appease the IDE type-checker, but not our TSC lint CI job.
@@ -18,7 +18,9 @@ if (typeof abandon === 'function') { | |||
/** @param {Error} reason */ | |||
raise = reason => { | |||
// Check `console` each time `raise` is called. | |||
// eslint-disable-next-line no-restricted-globals |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@erights and I expressly decided that lazy binding console
and assert
and dynamic dispatch on their methods were the desired, albeit exceptional, behavior. The reason being that SES changes these on lockdown. The machinery to have lockdown incidentally change some shared module state is far too complex to warrant.
continue; | ||
} | ||
intrinsics[namePrototype] = intrinsicPrototype; | ||
const addIntrinsics = newIntrinsics => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The change below here is merely changing the intrinsicsCollector to a bag of closure functions instead of using concise methods. They were already used as bare closure functions, but this needed to be more explicit to avoid dynamic dispatch.
This is a case where dynamic dispatch would have been just as safe, but it was as easy to refactor this way and avoid lint-rule comment noise.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice!
82c38bf
to
fef1e82
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've created a monster 👹
(looks good)
I think we made a mistake prior to this PR that should be fixed before this PR: commons.js should not be capturing and reexporting things like Thus, these repair modules should do their own initial capture of these objects and not reexport them. But commons.js, capturing the originals and reexporting them, keeps them accessible rather than safely encapsulated. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At #827 (comment) I write:
I think we made a mistake prior to this PR that should be fixed before this PR: commons.js should not be capturing and reexporting things like
Error
which, if captured this early, would be the originalError
constructor that is supposed to be completely inaccessible after repair, even in the start compartment. The originalError
constructor should be completely encapsulated within the repairedError
constructors.Thus, these repair modules should do their own initial capture of these objects and not reexport them. But commons.js, capturing the originals and reexporting them, keeps them accessible rather than safely encapsulated.
I'm posting that as a "request changes" so it is repaired before this PR proceeds.
Just eyeballing commons.js quickly, the violations that jump out at me:
We should either remove all four from commons.js, or we should follow the Note that The thread above already alerts us to the issue of the non-standard powerful SES-shim globals Upcoming dangerous standard globals for us to eventually incorporate: So, for now, unless I missed something, just exporting the original |
fef1e82
to
67d0117
Compare
67d0117
to
b1123b9
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The FERAL_ naming only works if it occurs rarely enough to set off alarm bells when you see it. Most of the occurrences here are just throw FERAL_ERROR
which probably should have been throw TypeError
anyway as that is the more usual choice. With those fixed, the problem is much reduced, and the phrase will continue to alarm.
packages/ses/index.js
Outdated
@@ -12,7 +12,7 @@ | |||
// See the License for the specific language governing permissions and | |||
// limitations under the License. | |||
|
|||
import { globalThis, Error, assign } from './src/commons.js'; | |||
import { globalThis, FERAL_ERROR, assign } from './src/commons.js'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See note below
import { globalThis, FERAL_ERROR, assign } from './src/commons.js'; | |
import { globalThis, TypeError, assign } from './src/commons.js'; |
@@ -0,0 +1,72 @@ | |||
/// <reference types="ses"> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I (MarkM) did not review this refactoring.
@@ -194,7 +196,7 @@ export default function enablePropertyOverrides( | |||
break; | |||
} | |||
default: { | |||
throw new Error(`unrecognized overrideTaming ${overrideTaming}`); | |||
throw new FERAL_ERROR(`unrecognized overrideTaming ${overrideTaming}`); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TypeError
packages/ses/src/module-link.js
Outdated
@@ -163,7 +163,7 @@ export const instantiate = ( | |||
resolvedImports, | |||
); | |||
} else { | |||
throw new Error( | |||
throw new FERAL_ERROR( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TypeError
packages/ses/src/scope-handler.js
Outdated
console.warn( | ||
`getOwnPropertyDescriptor trap on scopeHandler for ${quotedProp}`, | ||
new Error().stack, | ||
new FERAL_ERROR().stack, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any error would do equally well here.
new FERAL_ERROR().stack, | |
new TypeError().stack, |
@@ -39,7 +40,7 @@ const nonLocaleCompare = tamedMethods.localeCompare; | |||
|
|||
export default function tameLocaleMethods(intrinsics, localeTaming = 'safe') { | |||
if (localeTaming !== 'safe' && localeTaming !== 'unsafe') { | |||
throw new Error(`unrecognized dateTaming ${localeTaming}`); | |||
throw new FERAL_ERROR(`unrecognized dateTaming ${localeTaming}`); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TypeError
Error, | ||
RegExp as OriginalRegExp, | ||
FERAL_ERROR, | ||
FERAL_REG_EXP, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the one occurrence I expected
@@ -114,7 +116,9 @@ export default function whitelistIntrinsics( | |||
} | |||
|
|||
// We can't clean [[prototype]], therefore abort. | |||
throw new Error(`Unexpected intrinsic ${path}.__proto__ at ${protoName}`); | |||
throw new FERAL_ERROR( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TypeError
7e63539
to
52edbbc
Compare
52edbbc
to
4c33308
Compare
4c33308
to
0c72ad6
Compare
All your feedback on minimizing invocations of |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM. Thanks!
packages/ses/src/commons.js
Outdated
* isError tests whether an object descends from the intrinsic Error | ||
* constructor. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know what "descends" means here.
* isError tests whether an object descends from the intrinsic Error | |
* constructor. | |
* isError tests whether an object inherits from the intrinsic | |
* `Error.prototype`. |
packages/ses/src/commons.js
Outdated
* signal for reviewers that we are handling an object with excess authority | ||
* that we are carefully hiding from client code, like stack trace inspection. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
* signal for reviewers that we are handling an object with excess authority | |
* that we are carefully hiding from client code, like stack trace inspection. | |
* signal for reviewers that we are handling an object with excess authority, | |
* like stack trace inspection, that we are carefully hiding from client code. |
avoids a parsing ambiguity.
@@ -214,21 +226,21 @@ const tagError = (err, optErrorName = err.name) => { | |||
*/ | |||
const makeError = ( | |||
optDetails = redactedDetails`Assert failed`, | |||
ErrorConstructor = Error, | |||
ErrorConstructor = globalThis.Error, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
good
@@ -39,7 +39,7 @@ const nonLocaleCompare = tamedMethods.localeCompare; | |||
|
|||
export default function tameLocaleMethods(intrinsics, localeTaming = 'safe') { | |||
if (localeTaming !== 'safe' && localeTaming !== 'unsafe') { | |||
throw new Error(`unrecognized dateTaming ${localeTaming}`); | |||
throw new TypeError(`unrecognized dateTaming ${localeTaming}`); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
throw new TypeError(`unrecognized dateTaming ${localeTaming}`); | |
throw new TypeError(`unrecognized localeTaming ${localeTaming}`); |
I'm sure this was my mistake, but is a good time to fix it.
@@ -39,7 +39,7 @@ const nonLocaleCompare = tamedMethods.localeCompare; | |||
|
|||
export default function tameLocaleMethods(intrinsics, localeTaming = 'safe') { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We still need a special case for Number.toLocaleString
, to avoid the same bug that XS had. Yes?
0c72ad6
to
0273165
Compare
[edit kriskowal 2021-07-15]
This adds an eslint plugin that @kumavis authored for us to help us validate that SES shim captures all of the behavior of intrinsics that it needs during initialization in order to provide a faithful emulation of a native SES implementation, to the extent that’s possible and given that SES runs before any intrinsic is altered away from specified behavior.
Then follows changes by me to get SES shim to obey the lint rule.
@erights and I concluded that console and assert should in general be bound late and their methods dispatched dynamically.
Some compartment code moved around to prevent alteration of the Compartment prototype from affecting the behavior of “native” compartment methods.