Repository navigation
Linker always uses the latest security schema: img|src/video|src sanitizers dropped for Angular < 22.2.2 #586
Description
Summary
Since #577 (0.0.41), the compiler matches Angular 22.2.2+, which removed img|src and video|src
from the URL security context (angular/angular@439ff1bdf4). That's gated on angularVersion, but:
- The linker can't be told the version.
linkAngularPackage(code, filename)takes no
options, andangular-linker-plugin.jscalls it without the plugin'sangularVersion. Every
partially compiled library innode_modulesis linked with the 22.2.2+ schema, whatever
Angular the app runs. - The default is "latest". With
angularVersionunset (the README examples omit it), app
code is also compiled with the 22.2.2+ schema even when the installed@angular/coreis older.
The emit change only works with a matching runtime. angular/angular@439ff1bdf4 also added
unwrapImgVideoSrcValue() to setDomProperty/elementAttributeInternal in @angular/core. That
shim unwraps SafeValues and maps null/undefined to '' for img/video src, because the
sanitizer used to do both. On @angular/core ≤ 22.2.1 the shim doesn't exist. Code emitted for
22.2.2+ then assigns a SafeUrl from DomSanitizer.bypassSecurityTrustUrl() as the raw
SafeValue object (and null as "null"). The image fails to load, (load) never fires, and
nothing reports an error. Untrusted URLs also skip the sanitization ngc for that version applies.
Reproduction (@oxc-angular/vite@0.0.41, installed @angular/core@22.2.0)
Linker:
import { linkAngularPackage } from '@oxc-angular/vite/api';
const code = `import * as i0 from '@angular/core';
class Cmp {
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.2.0", type: Cmp,
isStandalone: true, selector: "x-cmp", ngImport: i0,
template: '<img [src]="url"><video [src]="url"></video><a [href]="url"></a>' });
}
export { Cmp };`;
console.log((await linkAngularPackage(code, '/node_modules/x-lib/fesm2022/x-lib.mjs')).code);Actual:
i0.ɵɵproperty("src", ctx.url); // img: no sanitizer
i0.ɵɵproperty("src", ctx.url); // video: no sanitizer
i0.ɵɵproperty("href", ctx.url, i0.ɵɵsanitizeUrl);Expected for an app on Angular 22.2.0: i0.ɵɵproperty("src", ctx.url, i0.ɵɵsanitizeUrl) for
img and video, as ngc/@angular/compiler-cli 22.2.0 emits.
Compiler, for comparison:
transformAngularFile(src, id, {}, res) // ɵɵproperty("src", ctx.u)
transformAngularFile(src, id, { angularVersion: { major: 22, minor: 2, patch: 0 } }, res)
// ɵɵproperty("src", ctx.u, i0.ɵɵsanitizeUrl)Real-world impact
ngx-image-cropper 9.1.7 binds [src]="safeImgDataUrl()" (a SafeUrl). Under OXC, the cropper
never loads its image and imageCropped never emits. That failed 4 component tests in our
monorepo: the cropper and an upload-photo dialog built on it. Setting angularVersion doesn't
help, because the library goes through the linker.
Suggested fix
- Thread
angularVersioninto the linker (linkAngularPackage(code, filename, { angularVersion }))
and pass it fromangular-linker-plugin, including theoptimizeDepsrolldown plugin path. - When
angularVersionis unset, detect the installed@angular/coreversion (resolve
@angular/core/package.jsonfrom the project root) instead of assuming latest, so a minor
bump of OXC can't silently change emitted security behavior.
Environment
@oxc-angular/vite0.0.41 (linux-x64-gnu binding)- Angular 22.2.0, Vite 7.3.6
Metadata
Metadata
Assignees
Labels
Type
Fields
Priority
Activity