Skip to content

compartment-mapper: Compartment Map Transforms #2894

@boneskull

Description

@boneskull

What is the Problem Being Solved?

Currently, mapNodeModules() accepts a policy, which—if present—it uses to omit ModuleDescriptors from the resulting CompartmentDescriptor.module. Specifically, if a package policy exists for the package being crawled, it will only add ModuleDescriptor entries for those packages (which are dependencies) mentioned in the policy. If no package policy is found, then all dependencies are added.

Compartment Map Transform

I'm proposing a new concept: the Compartment Map Transform.

The Compartment Map Transform accepts a CompartmentMapDescriptor and options (including policy), performs operations on the CompartmentMapDescriptor, then returns it (probably easiest to actually mutate it in-place since the object is rather deep). Async transforms can be supported unless there's a specific reason not to.

mapNodeModules() would accept a new option, compartmentTransforms of type CompartmentMapTransformFn[]. Before resolving, mapNodeModules() would apply each of these in order, returning the final result.

For backwards compatibility, the default value of compartmentTransforms should be an array containing a new transform which mimics the current behavior (considering package policy) by omitting values from the CompartmentMapDescriptor.modules objects. We can call this function enforcePolicyCompartmentMapTransform for lack of a better idea. This function will be included in the public API so that users can concatenate it with other transforms. translateGraph() would change such that it is no longer in the business of checking policy.

Optional New Transform

While we can omit ModuleDescriptors based on policy, it'd be nice to be able to create ModuleDescriptors based on policy! Currently, ModuleDescriptors are not created without an associated entry in the dependency graph.

In the case of dynamic imports/requires, a situation arises wherein a package A instructed by package B to load package C. There is no other relationship between package A and package C; there is no dependency relationship described in the package descriptor(s).

Example: a legacy ESLint config, where ESLint (B) loads an .eslintrc file (A) which is configured to use a the @endo/ses plugin (C).

I'd like to create a new transform (and maybe even provide it from @endo/compartment-mapper) which would create ModuleDescriptor references in the CompartmentMapDescriptor based on the presence of a canonical name (mapping to an extant CompartmentDescriptor) within package policy.

This transform would not create new CompartmentDescriptors, because in the example, all of the packages live in the dependency graph somewhere.

Such a transform may be functionally equivalent to the solution in #2893, as I expect it will need to flip retained bits.

Description of the Design

  • A Compartment Map Transform could look like this:

    /**
     * @template T Base options bag
     * @template U Specific options bag
     */
    export type CompartmentMapTransformFn<T = MapNodeModulesOptions> =
      <U extends T = T>(compartmentMap: 
        CompartmentMapDescriptor, options?: U) => CompartmentMapDescriptor | 
          Promise<CompartmentMapDescriptor>;

    The generics are here to allow association of a discrete transform with a specific set of options. Put another way, you could define a CompartmentMapTransformFn that only works with mapNodeModules() or functions accepting its options.

  • Provide an internal helper function to apply a CompartmentTranformFn[] to a CompartmentMapDescriptor.

  • Prior to returning in mapNodeModules(), call helper function

  • Implement a transform which omits keypairs from CompartmentDescriptor.modules based on policy and update translateGraph() accordingly

Security Considerations

n/a

Scaling Considerations

n/a

Test Plan

Hopefully we have a test that would catch a regression here, as this is intended to be backwards-compatible. Otherwise there should be nothing special about the tests.

Compatibility Considerations

n/a

Upgrade Considerations

  • Mention in NEWS.md
  • Consider tutorial or example documentation

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions