Skip to content

Commit 2597652

Browse files
committed
Using CSP nonce
1 parent 798104e commit 2597652

File tree

9 files changed

+48
-15
lines changed

9 files changed

+48
-15
lines changed

packages/altair-app/angular.json

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,10 @@
6969
"serve": {
7070
"builder": "@angular-devkit/build-angular:dev-server",
7171
"options": {
72-
"buildTarget": "altair:build:development"
72+
"buildTarget": "altair:build:development",
73+
"headers": {
74+
"Content-Security-Policy": "style-src 'self' 'nonce-change-me' 'sha256-47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU='"
75+
}
7376
},
7477
"configurations": {
7578
"production": {
@@ -123,9 +126,7 @@
123126
}
124127
},
125128
"cli": {
126-
"schematicCollections": [
127-
"@angular-eslint/schematics"
128-
],
129+
"schematicCollections": ["@angular-eslint/schematics"],
129130
"packageManager": "pnpm",
130131
"analytics": false
131132
}

packages/altair-app/src/app/modules/altair/altair.module.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
APP_INITIALIZER,
88
ApplicationInitStatus,
99
ModuleWithProviders,
10+
CSP_NONCE,
1011
} from '@angular/core';
1112
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
1213
import {
@@ -59,6 +60,7 @@ import { RootState } from 'altair-graphql-core/build/types/state/state.interface
5960
import { AccountEffects } from './effects/account.effect';
6061
import { WorkspaceEffects } from './effects/workspace.effect';
6162
import { ElectronEffects } from './effects/electron.effect';
63+
import { AltairConfig } from 'altair-graphql-core/build/config';
6264

6365
registerLocaleData(en);
6466

@@ -203,6 +205,11 @@ export class AltairModule {
203205
provide: reducerToken,
204206
useValue: getReducer(),
205207
},
208+
{
209+
provide: CSP_NONCE,
210+
useFactory: (altairConfig: AltairConfig) => altairConfig.cspNonce,
211+
deps: [AltairConfig],
212+
},
206213
],
207214
};
208215
}

packages/altair-app/src/app/modules/altair/components/codemirror/codemirror.component.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ import {
5050
import { tags as t } from '@lezer/highlight';
5151
import { InternalEditorError } from '../../utils/errors';
5252
import { debug } from '../../utils/logger';
53+
import { AltairConfig } from 'altair-graphql-core/build/config';
5354

5455
@Component({
5556
selector: 'app-codemirror',
@@ -85,7 +86,10 @@ export class CodemirrorComponent
8586
private onTouched = () => {};
8687
private onChange = (s: string) => {};
8788

88-
constructor(private zone: NgZone) {}
89+
constructor(
90+
private zone: NgZone,
91+
private altairConfig: AltairConfig
92+
) {}
8993

9094
ngAfterViewInit() {
9195
this.zone.runOutsideAngular(() => {
@@ -407,6 +411,7 @@ export class CodemirrorComponent
407411
this.wrapLines ? EditorView.lineWrapping : [], // TODO: Create own compartment
408412
drawSelection(),
409413
EditorState.allowMultipleSelections.of(true),
414+
EditorView.cspNonce.of(this.altairConfig.cspNonce),
410415
bracketMatching(),
411416
closeBrackets(),
412417
history(),

packages/altair-app/src/app/modules/altair/containers/altair/altair.component.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
[appTheme]="theme$ | async"
44
[appDarkTheme]="themeDark$ | async"
55
[appAccentColor]="accentColor$ | async"
6+
[cspNonce]="cspNonce"
67
appFileDrop
78
(fileDroppedChange)="fileDropped($event)"
89
>

packages/altair-app/src/app/modules/altair/containers/altair/altair.component.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,8 @@ export class AltairComponent {
121121

122122
appVersion = environment.version;
123123

124+
cspNonce = '';
125+
124126
sidebarPanels$: Observable<AltairPanel[]>;
125127
headerPanels$: Observable<AltairPanel[]>;
126128

@@ -146,6 +148,7 @@ export class AltairComponent {
146148
) {
147149
this.isWebApp = altairConfig.isWebApp;
148150
this.authEnabled = !altairConfig.initialData.disableAccount;
151+
this.cspNonce = altairConfig.cspNonce;
149152
this.settings$ = this.store
150153
.pipe(select('settings'))
151154
.pipe(distinctUntilChanged());

packages/altair-app/src/app/modules/altair/directives/theme/theme.directive.ts

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,7 @@
11
import { Directive, Input, OnInit, OnChanges, SimpleChanges } from '@angular/core';
2-
import {
3-
createTheme,
4-
hexToRgbStr,
5-
ICustomTheme,
6-
ITheme,
7-
getCSS,
8-
} from 'altair-graphql-core/build/theme';
2+
import { ICustomTheme, getCSS } from 'altair-graphql-core/build/theme';
93

10-
import { css } from '@emotion/css';
11-
import { ThemeRegistryService } from '../../services';
4+
import createEmotion, { Emotion } from '@emotion/css/create-instance';
125
import { NzConfigService } from 'ng-zorro-antd/core/config';
136

147
@Directive({
@@ -18,7 +11,9 @@ export class ThemeDirective implements OnInit, OnChanges {
1811
@Input() appTheme: ICustomTheme = {};
1912
@Input() appDarkTheme: ICustomTheme = {};
2013
@Input() appAccentColor = '';
14+
@Input() cspNonce = '';
2115

16+
private emotionInstance?: Emotion;
2217
private className = '';
2318

2419
constructor(private nzConfigService: NzConfigService) {}
@@ -46,7 +41,9 @@ export class ThemeDirective implements OnInit, OnChanges {
4641
appDarkTheme?: ICustomTheme,
4742
accentColor?: string
4843
) {
49-
return css(getCSS(appTheme, appDarkTheme, accentColor));
44+
return this.getEmotionInstance().css(
45+
getCSS(appTheme, appDarkTheme, accentColor)
46+
);
5047
}
5148

5249
applyTheme(theme: ICustomTheme, darkTheme?: ICustomTheme, accentColor?: string) {
@@ -71,4 +68,14 @@ export class ThemeDirective implements OnInit, OnChanges {
7168
this.className = this.getDynamicClassName(appTheme, appDarkTheme, accentColor);
7269
document.documentElement.classList.add(this.className);
7370
}
71+
72+
private getEmotionInstance() {
73+
if (!this.emotionInstance) {
74+
this.emotionInstance = createEmotion({
75+
key: 'altair-theme',
76+
nonce: this.cspNonce,
77+
});
78+
}
79+
return this.emotionInstance;
80+
}
7481
}

packages/altair-core/src/config/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ export class AltairConfig {
5656
themes = ['light', 'dark', 'dracula', 'system'];
5757
isTranslateMode = isTranslateMode;
5858
isWebApp = (window as TODO).__ALTAIR_WEB_APP__;
59+
cspNonce = '';
5960
initialData = {
6061
url: '',
6162
subscriptionsEndpoint: '',
@@ -105,7 +106,9 @@ export class AltairConfig {
105106
initialWindows = [],
106107
disableAccount = false,
107108
initialAuthorization,
109+
cspNonce = 'change-me',
108110
}: AltairConfigOptions = {}) {
111+
this.cspNonce = cspNonce;
109112
this.initialData.url =
110113
(window as TODO).__ALTAIR_ENDPOINT_URL__ ?? endpointURL ?? '';
111114
this.initialData.subscriptionsEndpoint =

packages/altair-core/src/config/options.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,4 +145,9 @@ export interface AltairConfigOptions extends AltairWindowOptions {
145145
* Disable the account and remote syncing functionality
146146
*/
147147
disableAccount?: boolean;
148+
149+
/**
150+
* CSP nonce value to be used in Altair
151+
*/
152+
cspNonce?: string;
148153
}

packages/altair-static/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ const optionsProperties: AltairConfigOptionsObject = {
4343
persistedSettings: undefined,
4444
initialName: undefined,
4545
initialAuthorization: undefined,
46+
cspNonce: undefined,
4647
};
4748
const allowedProperties = Object.keys(
4849
optionsProperties

0 commit comments

Comments
 (0)