diff --git a/modules/ROOT/nav.adoc b/modules/ROOT/nav.adoc index 3ca8f98fb..76c63c92b 100644 --- a/modules/ROOT/nav.adoc +++ b/modules/ROOT/nav.adoc @@ -49,6 +49,7 @@ ** xref:dataweave-memory-management.adoc[Memory Management] ** xref:dataweave-logging-configuration.adoc[Logging Configuration] ** xref:dataweave-versioning-behavior.adoc[Versioning Behavior in DataWeave] + ** xref:dataweave-scope-visibility.adoc[Scope Visibility in DataWeave] * xref:dataweave-cookbook.adoc[DataWeave Examples] ** xref:dataweave-cookbook-extract-data.adoc[Extract Data] ** xref:dataweave-cookbook-select-xml-elements.adoc[Select XML Elements] diff --git a/modules/ROOT/pages/_partials/nav-dw.adoc b/modules/ROOT/pages/_partials/nav-dw.adoc index 9bd115e0e..9f971236e 100644 --- a/modules/ROOT/pages/_partials/nav-dw.adoc +++ b/modules/ROOT/pages/_partials/nav-dw.adoc @@ -253,6 +253,8 @@ ** xref:dw-dataformat.adoc[dw::extension::DataFormat] *** xref:dw-dataformat-types.adoc[DataFormat Types] *** xref:dw-dataformat-annotations.adoc[DataFormat Annotations] +** xref:dw-component.adoc[dw::meta::Component] +*** xref:dw-component-types.adoc[Component Types] ** xref:dw-mime.adoc[dw::module::Mime] *** xref:dw-mime-functions-fromstring.adoc[fromString] *** xref:dw-mime-functions-ishandledby.adoc[isHandledBy] diff --git a/modules/ROOT/pages/dataweave-extension-plugin.adoc b/modules/ROOT/pages/dataweave-extension-plugin.adoc index 2f4e25ab0..d7caa0091 100644 --- a/modules/ROOT/pages/dataweave-extension-plugin.adoc +++ b/modules/ROOT/pages/dataweave-extension-plugin.adoc @@ -396,6 +396,22 @@ To check the logs, follow these steps: image::dataweave-extension-logs.png[Mule DX DataWeave LS option to select in the drop-down menu of the Output] +== Scope Visibility (DataWeave 2.12.0 and Later) + +When you author DataWeave *libraries* — `.dwl` files that other DataWeave code imports — the DataWeave extension recognizes the new visibility model added in 2.12.0: + +* The keywords `private` and `internal` are highlighted as language keywords. +* The `@VisibleTo` annotation is recognized when applied to `internal` directives. +* The editor reports visibility violations inline, with the same wording the compiler uses (`... cannot be accessed from ... as it is private to the module ...`, `... is internal to the component ...`). +* Auto-completion filters out directives that are not visible from the current file. For example, `private` helpers from another file will not appear in the suggestion list. +* `Go to definition` continues to work, but the editor warns when you navigate to a directive that is not accessible from the current file. + +These features apply to library projects only. If you are writing a Mule *transformation mapping* inline (a `%dw 2.0` expression in a Transform Message component, a Set Variable expression, or a DataWeave expression embedded in a connector), the `internal` keyword and `@VisibleTo` annotation have no practical effect: an inline mapping is a self-contained script that cannot be imported from another inline mapping, so there is nothing to constrain. The keywords are still parsed, but you do not need them in mapping code. + +The same applies to `.dwl` modules defined inside the Mule application. Every inline mapping and application-level module belongs to an "unnamed" component, so they can freely reference each other's `internal` directives. The `private` keyword still applies — it restricts a directive to its declaring file regardless of component. + +For the full visibility model — what `private` and `internal` mean, what a *component* is, and how the component descriptor is generated by the DataWeave Maven plugin — see xref:dataweave::dataweave-scope-visibility.adoc[Scope Visibility in DataWeave]. + == See Also * xref:dataweave-extension.adoc[] diff --git a/modules/ROOT/pages/dataweave-maven-plugin.adoc b/modules/ROOT/pages/dataweave-maven-plugin.adoc index 0b3bf07d0..e847de5c1 100644 --- a/modules/ROOT/pages/dataweave-maven-plugin.adoc +++ b/modules/ROOT/pages/dataweave-maven-plugin.adoc @@ -151,6 +151,53 @@ include::dataweave::partial$dataweave-maven-plugin-config.adoc[tag=config] ---- +== Configure Cryptographic Taint Analysis + +The Maven plugin for DataWeave can run cryptographic taint analysis at compile time to help identify potential security vulnerabilities in your DataWeave code. The analysis tracks literal algorithm values that flow into parameters annotated with xref:dataweave::dw-crypto-annotations.adoc[`@CryptographicSink`] and fails the build when an algorithm matches the configured insecure list. See xref:dataweave::dw-crypto-annotations.adoc[Crypto Annotations] for details on the underlying annotation. + +Inside the `plugin` configuration, add a `cryptoTaintAnalysisSettings` element configured to use parameters that meet your security needs: + +[%header%autowidth.spread,cols=".^a,.^a,.^a,.^a"] +|=== +|Name |Type |Default |Description + +|`enabled` +|`boolean` +|`false` +|When this property is set to `true`, the plugin runs cryptographic taint analysis during compilation. + +|`insecureAlgorithms` +|`String` +|`""` +|Comma-separated list of cryptographic algorithms considered insecure. The plugin reports a compilation error when one of these algorithms reaches a `@CryptographicSink` parameter. For example, `MD5,SHA1,DES,RC4`. + +|`sensitiveModules` +|`String` +|`""` +|Comma-separated list of DataWeave modules to flag as sensitive. Use DataWeave module notation with the `::` separator. For example, `dw::crypto::CryptoModule,dw::security::HashModule`. +|=== + +=== Cryptographic Taint Analysis Example + +The following example enables taint analysis and configures both the insecure algorithms and the sensitive modules to track: + +.Example `pom.xml` file +[source,xml,linenums] +---- + +include::dataweave::partial$dataweave-maven-plugin-config.adoc[tag=config] + + ... + + true + MD5,SHA1,DES,RC4 + dw::crypto::CryptoModule,dw::security::HashModule,dw::util::SecurityModule + + ... + + +---- + == Generate Documentation for a DataWeave Library Run the `data-weave:generate-docs` goal to auto-generate the documentation for your DataWeave library, for example: diff --git a/modules/ROOT/pages/dataweave-scope-visibility.adoc b/modules/ROOT/pages/dataweave-scope-visibility.adoc new file mode 100644 index 000000000..470470154 --- /dev/null +++ b/modules/ROOT/pages/dataweave-scope-visibility.adoc @@ -0,0 +1,236 @@ += Scope Visibility in DataWeave +:page-aliases: dataweave::dataweave-scope-visibility.adoc + +DataWeave 2.12.0 introduces *scope visibility*: a way to control which directives in a DataWeave file can be referenced from other files. Visibility rules apply to module-level declarations: `fun`, `var`, `type`, `ns`, and `annotation`. + +Scope visibility is intended primarily for *DataWeave library authors*. If you write Mule transformation mappings (the `%dw 2.0` script in a Transform Message component), you can ignore this feature: a mapping is a single, self-contained file that does not export anything to other files. Visibility becomes useful only when you publish a `.jar` that other DataWeave code imports. + +== Visibility Levels + +DataWeave 2.12.0 supports three visibility levels: + +[%header%autowidth.spread] +|=== +| Level | Keyword | Accessible from +| Public | _(none)_ | Anywhere. Default and only behavior in versions before 2.12.0. +| Private | `private` | The same file only. +| Internal | `internal` | Any file in the same xref:#components[component]. Can be widened with `@VisibleTo`. +|=== + +[NOTE] +==== +Visibility checks run only at *compile time*. Once a script is compiled, no runtime visibility check is performed. This keeps execution overhead at zero and ensures pre-compiled (`.bdwl`) modules behave the same as source modules. +==== + +== Syntax + +Place the visibility keyword before the directive keyword (`fun`, `var`, `type`, `ns`, `annotation`). + +[source,dataweave] +---- +%dw 2.0 + +// Public (default) — accessible from anywhere +fun greet(name: String) = "Hello, $(name)!" + +// Private — accessible only inside this file +private fun normalize(name: String) = upper(name) + +// Internal — accessible from any file in the same component +internal var defaultGreeting = "Hello" + +// Type and namespace declarations also support visibility +private type UserName = String +internal ns http https://schemas.example.org/http +---- + +The `@VisibleTo` annotation widens an `internal` directive to a fixed list of additional components: + +[source,dataweave] +---- +%dw 2.0 +import * from dw::Core + +@VisibleTo(components = ["analytics", "reporting"]) +internal fun computeStats(records: Array) = sizeOf(records) +---- + +`@VisibleTo` is only valid on `internal` directives. Applying it to a `private` directive or to a directive without a visibility keyword raises a compile-time error: `The @VisibleTo annotation can only be applied to 'internal' directives`. + +[#components] +== What Is a Component? + +DataWeave does not have a `module` or `package` keyword that groups files together. Until 2.12.0, every `.dwl` file lived in a single global namespace. To make `internal` meaningful, the language now uses the concept of a *component*: a named set of DataWeave resources (modules) that are compiled and packaged together. + +A component is described by a *component descriptor* — a `.dwl` resource located at `META-INF/dw-components.dwl` inside a `.jar`. The descriptor is an array of components; each component lists the resources it owns: + +[source,dataweave] +---- +%dw 2.0 +--- +[ + { + name: "wlang", + resources: { + "dw::Core": {}, + "dw::Crypto": {}, + "dw::core::Arrays": {}, + "dw::core::Strings": {} + // ... + } + } +] +---- + +When the compiler resolves a reference to a symbol declared in another module, it looks up which component owns the *declaring* module and which component owns the *caller* module by consulting the descriptors on the classpath. + +=== Generating the Descriptor + +You don't write the descriptor by hand. The xref:dataweave::dataweave-maven-plugin.adoc[DataWeave Maven Plugin] generates it automatically: + +* For a production build (`data-weave:package`), the plugin scans `src/main/dw`, derives the resource names of every `.dwl` file, and writes `target/generated-dataweave-metadata/dw-components.dwl`. The file is added to the produced JAR at `META-INF/dw-components.dwl`. +* For a test build (`data-weave:test`), the plugin scans both `src/main/dw` and `src/test/dw`, so test files belong to the same component as the production sources they exercise. This lets tests call `internal` directives without further configuration. + +The component name defaults to the Maven `artifactId` of the project. + +=== Component Resolution Algorithm + +Given a reference from caller `A` to symbol `s` declared in module `B`: + +. The compiler looks up `componentOf(B)` from the descriptors on the classpath. +. If `s` is `private`, access is allowed only when `A == B`. +. If `s` is `internal`, access is allowed when `componentOf(A) == componentOf(B)`, or when `componentOf(A)` appears in the `@VisibleTo` list on `s`. +. If `s` is `public`, access is always allowed. + +If a module declares `internal` directives but no descriptor on the classpath claims it, the compiler emits the error `Module 'X' declares 'internal' members but has no component descriptor`. In that case, package the module with the DataWeave Maven Plugin or add a component descriptor that lists it. + +== Compile-Time Validation + +The compiler reports four new errors: + +[%header%autowidth.spread] +|=== +| Error kind | Triggered when +| `PrivateAccessViolation` | A reference targets a `private` directive declared in a different file. +| `InternalAccessViolation` | A reference targets an `internal` directive declared in a different component, when the caller's component is not in the directive's `@VisibleTo` list. +| `VisibleToRequiresInternal` | `@VisibleTo` is applied to a directive that is not `internal`. +| `MissingComponentForInternalDirective` | A module declares `internal` directives but no component descriptor on the classpath claims it. +|=== + +== Function Overloads and Visibility + +Function overloads must share the same visibility. Mixing `internal fun foo(x: String) = ...` and `fun foo(x: Number) = ...` is rejected with an error indicating the expected visibility (taken from the first overload). This guarantees that callers see a single, consistent visibility for any given function name. + +== Structural Types and Visibility Propagation + +DataWeave is a *structural* type system. Visibility on a type controls who can reference the type *by name*; it does not propagate to functions or variables that use the type in their signature. A caller can invoke a function whose signature mentions a `private` type as long as the caller does not name that type. + +[source,dataweave] +---- +// In component A +private type Account = { id: String } + +@VisibleTo(components = ["bat"]) +internal fun createAccount(account: Account) = account + +// In component B (bat) +import createAccount from componentA::Accounts + +// OK — structural call, no explicit reference to `Account` +createAccount({ id: "abc" }) + +// Error — explicit reference to private type `Account` +createAccount({ id: "abc" }) as Account +---- + +The same rule applies to type annotations on variables (`var x: Account = ...`) and pattern matches that name a type. + +== Examples + +=== File-Local Helper + +[source,dataweave] +---- +// dw/core/Core.dwl + +%dw 2.0 + +private fun isLegacyMode(): Boolean = + evaluateCompatibilityFlag("com.mulesoft.dw.legacyMode") + +fun sizeOf(value) = + if (isLegacyMode()) legacyImpl(value) else newImpl(value) +---- + +`isLegacyMode` is reachable only inside `Core.dwl`. + +=== Component-Internal Helper + +[source,dataweave] +---- +// dw/core/Internal.dwl (component: wlang) +%dw 2.0 +internal fun dropFrame(): Boolean = ... + +// dw/core/Core.dwl (component: wlang) +%dw 2.0 +import dropFrame from dw::core::Internal +fun process() = if (dropFrame()) ... else ... // OK — same component + +// com/acme/App.dwl (component: acme-app) +%dw 2.0 +import dropFrame from dw::core::Internal // Error: internal to `wlang` +---- + +=== Sharing Internals With a Specific Component + +[source,dataweave] +---- +// dw/native/Native.dwl (component: wlang) +%dw 2.0 + +@VisibleTo(components = ["bat", "dataweave-io"]) +internal fun nativeImpl() = ... + +// bat/core/Runner.dwl (component: bat) +import nativeImpl from dw::native::Native // OK — bat is permitted + +// com/paypal/Connector.dwl (component: paypal-connector) +import nativeImpl from dw::native::Native // Error — not permitted +---- + +== Migrating From `@Internal(permits=...)` + +In versions before 2.12.0, the only way to constrain visibility was the experimental `@Internal(permits = [...])` annotation, which matched callers by `NameIdentifier` prefix. `@Internal` is still accepted in 2.12.0 but is deprecated in favor of the new keywords: + +[%header%autowidth.spread] +|=== +| Old form | Replacement +| `@Internal(permits = [])` | `private` +| `@Internal(permits = ["dw::"])` | `internal` (the consuming component must also be migrated to a 2.12.0 build so a descriptor exists) +| `@Internal(permits = ["bat::", "dw::"])` | `@VisibleTo(components = ["bat"])` `internal` +|=== + +Files that don't use any visibility keyword behave exactly as in earlier versions: every directive is public. + +== Setting a Per-Component Language Level + +When a host application embeds the DataWeave engine, it can pin a specific language level for each component using the new `componentLanguageLevels` parameter on `DWScriptingEngine.compileDWScript`: + +[source,scala] +---- +def compileDWScript( + script: String, + languageLevel: String, + componentLanguageLevels: util.Map[String, String] = new util.HashMap() +): DWScript +---- + +This is used together with `@Since` on function overloads to dispatch to the correct overload when a library evolves, ensuring backward compatibility for existing scripts. + +== See Also + +* xref:dataweave::dw-component.adoc[`dw::meta::Component` Module] +* xref:dataweave::dw-core-annotations.adoc[`@VisibleTo` Annotation] +* xref:dataweave::dataweave-maven-plugin.adoc[DataWeave Maven Plugin] +* xref:dataweave::dataweave-extension-plugin.adoc[DataWeave Extension Plugin] \ No newline at end of file diff --git a/modules/ROOT/pages/dw-component-types.adoc b/modules/ROOT/pages/dw-component-types.adoc new file mode 100644 index 000000000..9b60c7162 --- /dev/null +++ b/modules/ROOT/pages/dw-component-types.adoc @@ -0,0 +1,47 @@ += Component Types (dw::meta::Component) + +This module defines the DataWeave types for component descriptors. + +[%header, cols="1,2a,3a"] +|=== +| Type | Definition | Description + +| ComponentDescriptor +| `type ComponentDescriptor = { name: ComponentName, resources: { (NameIdentifier): ModuleDescriptor } }` +| Describes a single component. Contains the following fields: + +* `name`: Component identifier referenced from `@VisibleTo`. +* `resources`: Modules owned by the component, keyed by `NameIdentifier`. + +_Introduced in DataWeave version 2.12.0._ + + +| ComponentName +| `type ComponentName = String` +| Identifier of a component, matching the `name` field of a `ComponentDescriptor`. + +_Introduced in DataWeave version 2.12.0._ + + +| ComponentsDescriptor +| `type ComponentsDescriptor = Array`. The DataWeave Maven plugin generates it +automatically when packaging a `dw-library` artifact. + + +== Types + +* xref:dw-component-types.adoc[Component Types] \ No newline at end of file diff --git a/modules/ROOT/pages/dw-core-annotations.adoc b/modules/ROOT/pages/dw-core-annotations.adoc index edadd6ad3..2efb1d601 100644 --- a/modules/ROOT/pages/dw-core-annotations.adoc +++ b/modules/ROOT/pages/dw-core-annotations.adoc @@ -118,5 +118,30 @@ this annotation is not tail recursive, the function will fail. no privileges. For example, such a script cannot gain access to environment variables or read a resource from a URL. +_Experimental:_ This experimental feature is subject to change or removal from future versions of DataWeave. + +| VisibleTo +| `@VisibleTo(components: Array