feat(@endo/harden): Lightweight pre-lockdown hardened module support#2976
feat(@endo/harden): Lightweight pre-lockdown hardened module support#2976
Conversation
23d32db to
ff78924
Compare
ff78924 to
1032d2f
Compare
michaelfig
left a comment
There was a problem hiding this comment.
Looking good! I was curious, so I took the time to review.
packages/harden/README.md
Outdated
| harden(myFunction); | ||
|
|
||
| export const MyConstructor = function MyConstructor() {}; | ||
| harden(MyConstructor.prototype); |
There was a problem hiding this comment.
I may be off-base, but this line seems a little misleading, IIUC the latest harden design. Under HardenedJS, isn't it supposed to at the very least recurse over own properties (i.e. Reflect.ownKeys(obj)), but not prototypes (i.e. Reflect.getPrototypeOf(obj))?
With that in mind, harden(MyConstructor.prototype) seems completely redundant, since .prototype is an own property of MyConstructor, since it will be hardened upon calling harden(MyConstructor).
Reading further, the proposed pre-lockdown harden is a no-op. If it's never recursive (not even under HardenedJS), it just seems like an attractive nuisance.
There was a problem hiding this comment.
I think you are right that this is not a good example. I may need to appeal to @mhofman for a good example of a case where a defensively hardened module accidentally leaves something mutable exposed until the first instance is hardened. I’m likely confused and it’s possible that such a case is only possible with a speculatively different combination of alternative harden behaviors.
| - Adds `Object[Symbol.for('harden')]`, a variation on `globalThis.harden` that | ||
| cannot be denied by endowment of an alternative `harden` to compartments. |
There was a problem hiding this comment.
Ahhhh. Now it makes sense! You may want to call this out in the packages/harden/README.md.
279bc3a to
e6c94e6
Compare
9fa1896 to
c6f88ff
Compare
| const harden = o => o; | ||
| harden.isFake = true; | ||
| harden.lockdownError = new Error( | ||
| 'Cannot lockdown (repairIntrinsics) because @endo/harden used before lockdown on this stack', |
There was a problem hiding this comment.
What does "on this stack" mean?
There was a problem hiding this comment.
Based on a partial review of #2978 , what I want from this PR is:
- Have the normal noses testing config not redact, so ALMOST ALL the changes to golden error message tests can revert.
- Abstract
harden.isFake || Object.isFrozen(...)into a reusable abstraction, and use it.
Together, these would restore the brevity and readability of most existing tests.
c6f88ff to
edf0502
Compare
Co-authored-by: Michael FIG <mfig@agoric.com>
Co-authored-by: Michael FIG <mfig@agoric.com>
edf0502 to
2ac8c5d
Compare
|
Now favoring third approach #3008 |
|
Notably, the new approach relies on #3005 so tests using SES-AVA do not observe different error messages. |
Refs: #2983
Refs: #1686
Refs: #2782
Description
This very small change introduces an
@endo/hardenpackage that provides a fake harden by default, but in a way that ensures that it is never used in composition withlockdown.Description of the feature included in the PR documents.
While this avoids the need to eject the
hardeninternals ofsesinto another package, along with the complications that entrained in #2782, we may elect to provide additional build conditions for@endo/hardenthat provide greater degrees of immutability to applications that opt-in, either for their own safety or for earlier detection of inadequate prototype hardening.Security Considerations
Currently, hardened modules can only be used after lockdown. This change introduces a new era where some hardened modules can be used before lockdown if the application forswears ever using lockdown in the same realm. If a hardened module is initialized before lockdown in error, this approach ensures that the developer gets a useful diagnostic with a stack pointed at the offending module.
Scaling Considerations
Modules that adopt
@endo/hardenwill be slightly larger. The overhead can be reduced considerably by using thebundle-source -C hardenedcondition, in which case the additional module will only be a few lines and any additional excess is in the package linkage.Documentation Considerations
This new feature is documented in the ses NEWS and the new harden module’s README.
Testing Considerations
This module required multiple modules to exercise the various destructive behaviors of lockdown relative to when
hardengets called.Compatibility Considerations
Hardened modules were not previously usable before lockdown. This change commits us to a course where modules can be used before lockdown. Also, lockdown on previous hardened modules was already inadequate to the task of identifying gaps where the hardening of a module was not rigorous enough to the extent that prototypes were frozen hardened before the first instance was hardened. This is not a regression but does not create progress on that front. For progress toward ensuring rigorous hardening, I propose future changes that introduce new
lockdownmodes and@endo/hardenbuild conditions that create the conditions for opt-in detection of inadequately hardened prototype chains.The provision of
harden.isFakeis intended to compose well with other libraries that sense this property as an indication that they’re in a less-immutable environment.Upgrade Considerations
This change creates some coordination risks between versions of harden captured in bundles with divergent behavior from host environments that provide harden. I believe all of the matrix of possible interactions is accounted for.
@endo/hardenwhich will defer toglobalThis.hardenon platforms using older SES. There are some possible challenges with bundle size changing, but the changes should be minimal, and the build condition mitigation provided is not likely to make a big difference.globalThis.hardendirectly.Object[@harden]intrinsic that new code may come to rely upon. This new code will not be portable to older SES platforms. However, using a symbol and providing@endo/hardenas a typed facade should discourage the creation of incompatible code.