Skip to content

Commit a43c077

Browse files
atcastleAndrewKushnir
authored andcommitted
fix(common): Allow safeUrl for ngSrc in NgOptimizedImage (#51351)
Allow safeUrl and add transformer to immediately convert ngSrc to string PR Close #51351
1 parent bd4d2bb commit a43c077

File tree

3 files changed

+38
-2
lines changed

3 files changed

+38
-2
lines changed

goldens/public-api/common/index.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -617,6 +617,8 @@ export class NgOptimizedImage implements OnInit, OnChanges, OnDestroy {
617617
// (undocumented)
618618
static ngAcceptInputType_height: unknown;
619619
// (undocumented)
620+
static ngAcceptInputType_ngSrc: string | i1_2.SafeValue;
621+
// (undocumented)
620622
static ngAcceptInputType_priority: unknown;
621623
// (undocumented)
622624
static ngAcceptInputType_width: unknown;

packages/common/src/directives/ng_optimized_image/ng_optimized_image.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9-
import {booleanAttribute, Directive, ElementRef, inject, InjectionToken, Injector, Input, NgZone, numberAttribute, OnChanges, OnDestroy, OnInit, PLATFORM_ID, Renderer2, SimpleChanges, ɵformatRuntimeError as formatRuntimeError, ɵRuntimeError as RuntimeError} from '@angular/core';
9+
import {booleanAttribute, Directive, ElementRef, inject, InjectionToken, Injector, Input, NgZone, numberAttribute, OnChanges, OnDestroy, OnInit, PLATFORM_ID, Renderer2, SimpleChanges, ɵformatRuntimeError as formatRuntimeError, ɵRuntimeError as RuntimeError, ɵSafeValue as SafeValue, ɵunwrapSafeValue as unwrapSafeValue} from '@angular/core';
1010

1111
import {RuntimeErrorCode} from '../../errors';
1212
import {isPlatformServer} from '../../platform_id';
@@ -245,7 +245,7 @@ export class NgOptimizedImage implements OnInit, OnChanges, OnDestroy {
245245
* Image name will be processed by the image loader and the final URL will be applied as the `src`
246246
* property of the image.
247247
*/
248-
@Input({required: true}) ngSrc!: string;
248+
@Input({required: true, transform: unwrapSafeUrl}) ngSrc!: string;
249249

250250
/**
251251
* A comma separated list of width or density descriptors.
@@ -993,3 +993,12 @@ function assertNoLoaderParamsWithoutLoader(dir: NgOptimizedImage, imageLoader: I
993993
function round(input: number): number|string {
994994
return Number.isInteger(input) ? input : input.toFixed(2);
995995
}
996+
997+
// Transform function to handle SafeValue input for ngSrc. This doesn't do any sanitization,
998+
// as that is not needed for img.src and img.srcset. This transform is purely for compatibility.
999+
function unwrapSafeUrl(value: string|SafeValue): string {
1000+
if (typeof value === 'string') {
1001+
return value;
1002+
}
1003+
return unwrapSafeValue(value);
1004+
}

packages/common/test/directives/ng_optimized_image_spec.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {RuntimeErrorCode} from '@angular/common/src/errors';
1111
import {PLATFORM_SERVER_ID} from '@angular/common/src/platform_id';
1212
import {Component, PLATFORM_ID, Provider, Type} from '@angular/core';
1313
import {ComponentFixture, TestBed} from '@angular/core/testing';
14+
import {DomSanitizer, SafeResourceUrl} from '@angular/platform-browser';
1415
import {expect} from '@angular/platform-browser/testing/src/matchers';
1516
import {withHead} from '@angular/private/testing';
1617

@@ -720,6 +721,30 @@ describe('Image directive', () => {
720721
fixture.detectChanges();
721722
}).not.toThrowError(new RegExp('was updated after initialization'));
722723
});
724+
it('should accept a safeUrl ngSrc value', () => {
725+
@Component({
726+
selector: 'test-cmp',
727+
template: `<img
728+
[ngSrc]="bypassImage"
729+
width="400"
730+
height="600"
731+
>`
732+
})
733+
class TestComponent {
734+
rawImage = `javascript:alert("Hi there")`;
735+
bypassImage: SafeResourceUrl;
736+
constructor(private sanitizer: DomSanitizer) {
737+
this.bypassImage = sanitizer.bypassSecurityTrustResourceUrl(this.rawImage);
738+
}
739+
}
740+
setupTestingModule({component: TestComponent});
741+
const fixture = TestBed.createComponent(TestComponent);
742+
fixture.detectChanges();
743+
744+
let nativeElement = fixture.nativeElement as HTMLElement;
745+
let img = nativeElement.querySelector('img')!;
746+
expect(img.src).toContain(`${IMG_BASE_URL}/javascript:alert`);
747+
});
723748
});
724749

725750
describe('lazy loading', () => {

0 commit comments

Comments
 (0)