Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions angular.json
Original file line number Diff line number Diff line change
Expand Up @@ -419,8 +419,7 @@
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "src/test.ts",
"polyfills": "src/polyfills.ts",
"polyfills": ["src/polyfills.ts", "zone.js/testing"],
"tsConfig": "src/tsconfig.spec.json",
"karmaConfig": "src/karma.conf.js",
"styles": [
Expand Down
36 changes: 18 additions & 18 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,19 +39,19 @@
},
"private": true,
"dependencies": {
"@angular/animations": "20.1.6",
"@angular/cdk": "20.1.6",
"@angular/common": "20.1.6",
"@angular/compiler": "20.1.6",
"@angular/core": "20.1.6",
"@angular/elements": "20.1.6",
"@angular/forms": "20.1.6",
"@angular/localize": "20.1.6",
"@angular/material": "20.1.6",
"@angular/platform-browser": "20.1.6",
"@angular/platform-browser-dynamic": "20.1.6",
"@angular/router": "20.1.6",
"@angular/service-worker": "20.1.6",
"@angular/animations": "20.3.27",
"@angular/cdk": "20.2.14",
"@angular/common": "20.3.27",
"@angular/compiler": "20.3.27",
"@angular/core": "20.3.27",
"@angular/elements": "20.3.27",
"@angular/forms": "20.3.27",
"@angular/localize": "20.3.27",
"@angular/material": "20.2.14",
"@angular/platform-browser": "20.3.27",
"@angular/platform-browser-dynamic": "20.3.27",
"@angular/router": "20.3.27",
"@angular/service-worker": "20.3.27",
"@intl-tel-input/angular": "^29.0.1",
"@orcid/bibtex-parse-js": "0.0.25",
"@tailwindcss/postcss": "^4.1.18",
Expand All @@ -78,11 +78,11 @@
"zone.js": "~0.15.1"
},
"devDependencies": {
"@angular-devkit/build-angular": "20.1.6",
"@angular/build": "20.1.6",
"@angular/cli": "20.1.6",
"@angular/compiler-cli": "20.1.6",
"@angular/language-service": "20.1.6",
"@angular-devkit/build-angular": "20.3.33",
"@angular/build": "20.3.33",
"@angular/cli": "20.3.33",
"@angular/compiler-cli": "20.3.27",
"@angular/language-service": "20.3.27",
"@types/jasmine": "~3.6.0",
"@types/jasminewd2": "~2.0.3",
"@types/lodash": "^4.14.168",
Expand Down
11 changes: 6 additions & 5 deletions projects/orcid-ui-elements/src/app/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,7 @@ import {
DoBootstrap,
Injector,
} from '@angular/core'
import {
bootstrapApplication,
provideClientHydration,
} from '@angular/platform-browser'
import { bootstrapApplication } from '@angular/platform-browser'
import { createCustomElement } from '@angular/elements'
import { OrcidUi } from '@orcid/ui'

Expand All @@ -31,8 +28,12 @@ export class OrcidElementsBootstrapComponent implements DoBootstrap {
}
}

// No project in angular.json is built with SSR or prerendering, so client
// hydration has nothing to restore. Enabling it only activates the TransferState
// lookup of the `ng-state` element, which is the DOM-clobbering sink behind
// CVE-2026-54267 — bad trade for a bundle that embeds in third-party pages.
export const appConfig: ApplicationConfig = {
providers: [provideClientHydration()],
providers: [],
}

export function bootstrapOrcidElements() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ import { UserService } from '../../../core'
import { AccountActionsDuplicatedService } from '../../../core/account-actions-duplicated/account-actions-duplicated.service'

import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'
import { MatFormFieldModule } from '@angular/material/form-field'
import { MatInputModule } from '@angular/material/input'
import { NoopAnimationsModule } from '@angular/platform-browser/animations'

describe('SettingsActionsDuplicatedComponent', () => {
let component: SettingsActionsDuplicatedComponent
Expand All @@ -27,6 +30,9 @@ describe('SettingsActionsDuplicatedComponent', () => {
MatDialogModule,
RouterTestingModule,
ReactiveFormsModule,
MatFormFieldModule,
MatInputModule,
NoopAnimationsModule,
],
declarations: [SettingsActionsDuplicatedComponent],
providers: [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,27 @@ import { Overlay } from '@angular/cdk/overlay'
import { RouterTestingModule } from '@angular/router/testing'

import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'
import { ReactiveFormsModule } from '@angular/forms'
import { MatFormFieldModule } from '@angular/material/form-field'
import { MatInputModule } from '@angular/material/input'
import { MatIconModule } from '@angular/material/icon'
import { NoopAnimationsModule } from '@angular/platform-browser/animations'

describe('SettingsSecurityPasswordComponent', () => {
let component: SettingsSecurityPasswordComponent
let fixture: ComponentFixture<SettingsSecurityPasswordComponent>

beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [HttpClientTestingModule, RouterTestingModule],
imports: [
HttpClientTestingModule,
ReactiveFormsModule,
RouterTestingModule,
MatFormFieldModule,
MatInputModule,
MatIconModule,
NoopAnimationsModule,
],
declarations: [SettingsSecurityPasswordComponent],
providers: [
WINDOW_PROVIDERS,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,15 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'

import { DeepSelectInputComponent } from './deep-select-input.component'
import { PlatformInfoService } from '../../platform-info'
import { FormBuilder } from '@angular/forms'
import { FormBuilder, ReactiveFormsModule } from '@angular/forms'
import { get } from 'lodash'
import { of } from 'rxjs'
import { MatMenuModule } from '@angular/material/menu'
import { MatFormFieldModule } from '@angular/material/form-field'
import { MatInputModule } from '@angular/material/input'
import { MatIconModule } from '@angular/material/icon'
import { MatDividerModule } from '@angular/material/divider'
import { NoopAnimationsModule } from '@angular/platform-browser/animations'

describe('DeepSelectInputComponent', () => {
let component: DeepSelectInputComponent
Expand All @@ -21,18 +26,16 @@ describe('DeepSelectInputComponent', () => {
get: () => of({}),
},
},
{
provide: FormBuilder,
useValue: {
group: () => ({
get: () => ({
valueChanges: of(''),
}),
}),
},
},
],
imports: [MatMenuModule],
imports: [
MatMenuModule,
ReactiveFormsModule,
MatFormFieldModule,
MatInputModule,
MatIconModule,
MatDividerModule,
NoopAnimationsModule,
],
})
fixture = TestBed.createComponent(DeepSelectInputComponent)
component = fixture.componentInstance
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,19 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'

import { ShareEmailsDomainsComponent } from './share-emails-domains.component'
import { FormBuilder, FormControl, FormGroup } from '@angular/forms'
import {
FormBuilder,
FormControl,
FormGroup,
ReactiveFormsModule,
} from '@angular/forms'
import { RecordEmailsService } from 'src/app/core/record-emails/record-emails.service'

import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'
import { MatCardModule } from '@angular/material/card'
import { MatCheckboxModule } from '@angular/material/checkbox'
import { MatDividerModule } from '@angular/material/divider'
import { MatIconModule } from '@angular/material/icon'
import { UserService } from 'src/app/core'
import { PlatformInfoService } from 'src/app/cdk/platform-info/platform-info.service'
import { WINDOW_PROVIDERS } from 'src/app/cdk/window'
Expand All @@ -18,18 +27,18 @@ describe('ShareEmailsDomainsComponent', () => {
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [ShareEmailsDomainsComponent],
imports: [
MatCardModule,
ReactiveFormsModule,
MatCheckboxModule,
MatDividerModule,
MatIconModule,
],
providers: [
{
provide: PlatformInfoService,
useValue: {},
},
{
provide: FormBuilder,
useValue: {
array: () => [new FormControl({})],
group: () => new FormGroup({}),
},
},
{
provide: RecordEmailsService,
useValue: {},
Expand All @@ -47,7 +56,21 @@ describe('ShareEmailsDomainsComponent', () => {
{
provide: RecordService,
useValue: {
getRecord: () => of({}),
// The component only builds its form once a record carrying
// emailDomains arrives, so an empty record leaves `form` undefined
// and `[formGroup]` with nothing to bind to.
getRecord: () =>
of({
emails: {
emailDomains: [
{
value: 'example.org',
visibility: 'PRIVATE',
createdDate: { timestamp: 1 },
},
],
},
}),
},
},
WINDOW_PROVIDERS,
Expand Down
21 changes: 21 additions & 0 deletions src/app/core/xsrf/xsrf-fallback.interceptor.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,27 @@ describe('XsrfFallbackInterceptor', () => {
req.flush({})
})

it('passes through protocol-relative URLs pointing at a foreign host', () => {
cookieGetSpy.and.returnValue('token')
http.post('//other-origin.com/api', {}).subscribe()

const req = httpMock.expectOne('//other-origin.com/api')
expect(req.request.headers.has('x-xsrf-token')).toBe(false)
expect(req.request.withCredentials).toBe(false)
req.flush({})
})

it('still adds the token for a protocol-relative URL on the API host', () => {
cookieGetSpy.and.callFake((name: string) =>
name === 'XSRF-TOKEN' ? 'token' : ''
)
http.post('//api.example/works/work.json', {}).subscribe()

const req = httpMock.expectOne('//api.example/works/work.json')
expect(req.request.headers.get('x-xsrf-token')).toBe('token')
req.flush({})
})

it('passes through when cookie is missing', () => {
cookieGetSpy.and.returnValue('')

Expand Down
6 changes: 5 additions & 1 deletion src/app/core/xsrf/xsrf-fallback.interceptor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,11 @@ export class XsrfFallbackInterceptor implements HttpInterceptor {
const baseUrl = runtimeEnvironment.BASE_URL
const authBase = runtimeEnvironment.AUTH_SERVER

const isRelativeRequest = req.url.startsWith('/')
// A protocol-relative URL (`//host/path`) also starts with `/` but resolves
// to a foreign origin, so it must not be treated as a relative request —
// otherwise the token below is handed to whatever host the attacker names.
const isRelativeRequest =
req.url.startsWith('/') && !req.url.startsWith('//')
const requestUrl = this.toAbsoluteUrl(req.url)
const isApiHostCall = this.sameHost(req.url, apiBase)
const isBaseHostCall = this.sameHost(req.url, baseUrl)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'

import { PageNotFoundComponent } from './page-not-found.component'
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'

describe('PageNotFoundComponent', () => {
let component: PageNotFoundComponent
Expand All @@ -9,6 +10,7 @@ describe('PageNotFoundComponent', () => {
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [PageNotFoundComponent],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
fixture = TestBed.createComponent(PageNotFoundComponent)
component = fixture.componentInstance
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { SnackbarService } from 'src/app/cdk/snackbar/snackbar.service'
import { MatDialog } from '@angular/material/dialog'
import { MatSnackBar } from '@angular/material/snack-bar'
import { MonthDayYearDateToStringPipe } from 'src/app/shared/pipes/month-day-year-date-to-string/month-day-year-date-to-string.pipe'
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'

describe('WorkDetailsComponent', () => {
let component: WorkDetailsComponent
Expand All @@ -18,6 +19,7 @@ describe('WorkDetailsComponent', () => {
imports: [HttpClientTestingModule, RouterTestingModule],
declarations: [WorkDetailsComponent, MonthDayYearDateToStringPipe],
providers: [WINDOW_PROVIDERS, SnackbarService, MatSnackBar, MatDialog],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
fixture = TestBed.createComponent(WorkDetailsComponent)
component = fixture.componentInstance
Expand Down
17 changes: 0 additions & 17 deletions src/test.ts

This file was deleted.

2 changes: 1 addition & 1 deletion src/tsconfig.spec.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@
"outDir": "../out-tsc/spec",
"types": ["jasmine", "node"]
},
"files": ["test.ts", "polyfills.ts"],
"files": ["polyfills.ts"],
"include": ["**/*.spec.ts", "**/*.d.ts"]
}
Loading
Loading