emilsoftware-utilities 2.0.0-dev.75 → 2.0.0-dev.76
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/accessi-module/AccessiModule.d.ts +2 -1
- package/dist/accessi-module/AccessiModule.js.map +1 -1
- package/dist/accessi-module/Console/console.js +2 -2
- package/dist/accessi-module/Console/index.html +1 -1
- package/dist/accessi-module/Console/wiki.js +2 -2
- package/dist/accessi-module/Console/wiki.js.map +1 -1
- package/dist/accessi-module/Controllers/AccessiConsoleController.d.ts +5 -3
- package/dist/accessi-module/Controllers/AccessiConsoleController.js +39 -12
- package/dist/accessi-module/Controllers/AccessiConsoleController.js.map +1 -1
- package/dist/accessi-module/index.d.ts +2 -0
- package/dist/accessi-module/index.js +15 -14
- package/dist/accessi-module/index.js.map +1 -1
- package/dist/accessi-module/security/securityHeaders.d.ts +89 -0
- package/dist/accessi-module/security/securityHeaders.js +155 -0
- package/dist/accessi-module/security/securityHeaders.js.map +1 -0
- package/dist/emilsoftware-utilities-2.0.0-dev.76.tgz +0 -0
- package/dist/initEmilsoftwareModule.js +6 -11
- package/dist/initEmilsoftwareModule.js.map +1 -1
- package/docs/security-headers.md +151 -0
- package/package.json +3 -3
- package/dist/emilsoftware-utilities-2.0.0-dev.75.tgz +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"initEmilsoftwareModule.js","sourceRoot":"","sources":["../src/initEmilsoftwareModule.ts"],"names":[],"mappings":"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","sourcesContent":["import express, { Application, Request } from \"express\";\nimport { NestFactory } from \"@nestjs/core\";\nimport { ExpressAdapter } from \"@nestjs/platform-express\";\nimport type { INestApplication } from \"@nestjs/common\";\nimport type { OpenAPIObject } from \"@nestjs/swagger\";\nimport {\n buildOpenApiExcludePaths,\n isOpenApiRequestPath,\n resolveAccessiOpenApiOptions,\n setupOpenApi\n} from \"./openapi/OpenApiConfig\";\nimport { Logger } from \"./Logger\";\nimport { EmilsoftwareModule, EmilsoftwareOptions } from \"./EmilsoftwareModule\";\nimport {\n beginAccessiAuthInitialization,\n failAccessiAuthInitialization,\n setAccessiAuthService\n} from \"./accessi-module/middleware/authenticateGen\";\nimport { registerAccessiModuleServices } from \"./accessi-module/accessiRuntime\";\nimport { logAccessiStartup } from \"./accessi-module/startupLog\";\nimport { createAccessiValidationPipe } from './accessi-module/security/accessiValidation';\nimport { AccessiHttpExceptionFilter } from './accessi-module/security/AccessiHttpExceptionFilter';\nimport { getAccessiJwksPath, setupAccessiJwks } from './accessi-module/security/jwks';\nimport { createAccessiEdgeRateLimitMiddleware } from './accessi-module/security/edgeRateLimit';\n\n/** Esito del bootstrap del modulo unificato. */\nexport interface EmilsoftwareModuleInitializationResult {\n /** Sotto-app Nest che serve Accessi, Allegati e la documentazione. */\n nestApp: INestApplication;\n /** Documento OpenAPI generato, mergiabile nel documento unico dell'host. */\n document: OpenAPIObject;\n}\n\n/**\n * Initializes the unified module that combines Accessi and Allegati functionality\n * @param app Express application instance\n * @param options Configuration options for both modules\n * @returns Il sotto-app Nest e il documento OpenAPI generato.\n */\nexport async function initEmilsoftwareModule(\n app: Application,\n options: EmilsoftwareOptions\n): Promise<EmilsoftwareModuleInitializationResult> {\n const logger: Logger = new Logger(initEmilsoftwareModule.name);\n if (options?.accessiOptions) {\n beginAccessiAuthInitialization();\n }\n\n try {\n // Isolate Nest on a dedicated Express instance and dispatch only module routes.\n // This keeps pre-existing routes/middlewares on the host app untouched.\n // Prefisso OpenAPI configurabile (default `accessi/openapi`), cosi' piu'\n // documentazioni possono convivere (backend su `/openapi`, moduli sotto\n // `/<modulo>/openapi`).\n // Precedenza: EmilsoftwareOptions.openapi > AccessiOptions.openapi > default.\n const resolvedOpenApi = resolveAccessiOpenApiOptions(\n options?.accessiOptions?.openapi,\n options?.openapi\n );\n\n const nestHostApp = express();\n if (options?.accessiOptions?.http?.trustProxy !== undefined) {\n app.set('trust proxy', options.accessiOptions.http.trustProxy);\n nestHostApp.set('trust proxy', options.accessiOptions.http.trustProxy);\n }\n const jwksPath = options?.accessiOptions\n ? getAccessiJwksPath(options.accessiOptions)\n : undefined;\n const isNestRoute = (path: string) =>\n path === \"/api\" ||\n path.startsWith(\"/api/\") ||\n isOpenApiRequestPath(path, resolvedOpenApi) ||\n (jwksPath !== undefined && path === jwksPath);\n const edgeRateLimit = options?.accessiOptions?.edgeRateLimit\n ? createAccessiEdgeRateLimitMiddleware({\n ...options.accessiOptions.edgeRateLimit,\n match: options.accessiOptions.edgeRateLimit.match ?? ((path) => isNestRoute(path)),\n })\n : undefined;\n if (edgeRateLimit) {\n app.use(edgeRateLimit);\n }\n if (options?.accessiOptions?.http?.securityHeaders !== false) {\n app.use((req: Request, res, next) => {\n if (!isNestRoute(req.path)) {\n return next();\n }\n res.setHeader('X-Content-Type-Options', 'nosniff');\n res.setHeader('X-Frame-Options', 'SAMEORIGIN');\n res.setHeader('Referrer-Policy', 'no-referrer');\n res.setHeader('Permissions-Policy', 'camera=(), microphone=(), geolocation=()');\n return next();\n });\n }\n\n app.use((req: Request, res, next) => {\n if (!isNestRoute(req.path)) {\n return next();\n }\n return nestHostApp(req, res, next);\n });\n\n if (options?.accessiOptions) {\n setupAccessiJwks(app, options.accessiOptions);\n if (jwksPath) {\n logger.info(`JWKS accessi disponibile su: ${jwksPath}`);\n }\n }\n\n const nestExpressInstance = new ExpressAdapter(nestHostApp);\n const nestApp = await NestFactory.create(EmilsoftwareModule.forRoot(options), nestExpressInstance, {\n bufferLogs: options?.bufferLogs !== false,\n ...(options?.logger ? { logger: options.logger } : {})\n });\n\n if (options?.accessiOptions?.http?.cors !== false) {\n nestApp.enableCors(options?.accessiOptions?.http?.cors);\n }\n if (options?.accessiOptions) {\n // Keep the Accessi API contract identical for standalone and unified bootstraps.\n nestApp.useGlobalPipes(createAccessiValidationPipe());\n nestApp.useGlobalFilters(new AccessiHttpExceptionFilter());\n }\n\n nestApp.setGlobalPrefix('api', {\n exclude: buildOpenApiExcludePaths(resolvedOpenApi)\n });\n\n // Documentazione OpenAPI del modulo unificato (Accessi + Allegati).\n const document = setupOpenApi(nestApp, resolvedOpenApi);\n\n await nestApp.init();\n if (options?.accessiOptions) {\n const accessiServices = registerAccessiModuleServices(app, nestApp);\n app.locals.accessiAuthService = accessiServices.authenticateGenService;\n setAccessiAuthService(accessiServices.authenticateGenService);\n logAccessiStartup(options.accessiOptions, resolvedOpenApi);\n }\n\n logger.info(\"Emilsoftware module initialized successfully\");\n\n return { nestApp, document };\n } catch (error) {\n if (options?.accessiOptions) {\n failAccessiAuthInitialization(error);\n }\n logger.error(\"Error initializing Emilsoftware Module:\", error);\n throw error;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"initEmilsoftwareModule.js","sourceRoot":"","sources":["../src/initEmilsoftwareModule.ts"],"names":[],"mappings":"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","sourcesContent":["import express, { Application, Request } from \"express\";\nimport { NestFactory } from \"@nestjs/core\";\nimport { ExpressAdapter } from \"@nestjs/platform-express\";\nimport type { INestApplication } from \"@nestjs/common\";\nimport type { OpenAPIObject } from \"@nestjs/swagger\";\nimport {\n buildOpenApiExcludePaths,\n isOpenApiRequestPath,\n resolveAccessiOpenApiOptions,\n setupOpenApi\n} from \"./openapi/OpenApiConfig\";\nimport { Logger } from \"./Logger\";\nimport { EmilsoftwareModule, EmilsoftwareOptions } from \"./EmilsoftwareModule\";\nimport {\n beginAccessiAuthInitialization,\n failAccessiAuthInitialization,\n setAccessiAuthService\n} from \"./accessi-module/middleware/authenticateGen\";\nimport { registerAccessiModuleServices } from \"./accessi-module/accessiRuntime\";\nimport { logAccessiStartup } from \"./accessi-module/startupLog\";\nimport { createAccessiValidationPipe } from './accessi-module/security/accessiValidation';\nimport { AccessiHttpExceptionFilter } from './accessi-module/security/AccessiHttpExceptionFilter';\nimport { getAccessiJwksPath, setupAccessiJwks } from './accessi-module/security/jwks';\nimport { createAccessiEdgeRateLimitMiddleware } from './accessi-module/security/edgeRateLimit';\nimport { createAccessiSecurityHeadersMiddleware } from './accessi-module/security/securityHeaders';\n\n/** Esito del bootstrap del modulo unificato. */\nexport interface EmilsoftwareModuleInitializationResult {\n /** Sotto-app Nest che serve Accessi, Allegati e la documentazione. */\n nestApp: INestApplication;\n /** Documento OpenAPI generato, mergiabile nel documento unico dell'host. */\n document: OpenAPIObject;\n}\n\n/**\n * Initializes the unified module that combines Accessi and Allegati functionality\n * @param app Express application instance\n * @param options Configuration options for both modules\n * @returns Il sotto-app Nest e il documento OpenAPI generato.\n */\nexport async function initEmilsoftwareModule(\n app: Application,\n options: EmilsoftwareOptions\n): Promise<EmilsoftwareModuleInitializationResult> {\n const logger: Logger = new Logger(initEmilsoftwareModule.name);\n if (options?.accessiOptions) {\n beginAccessiAuthInitialization();\n }\n\n try {\n // Isolate Nest on a dedicated Express instance and dispatch only module routes.\n // This keeps pre-existing routes/middlewares on the host app untouched.\n // Prefisso OpenAPI configurabile (default `accessi/openapi`), cosi' piu'\n // documentazioni possono convivere (backend su `/openapi`, moduli sotto\n // `/<modulo>/openapi`).\n // Precedenza: EmilsoftwareOptions.openapi > AccessiOptions.openapi > default.\n const resolvedOpenApi = resolveAccessiOpenApiOptions(\n options?.accessiOptions?.openapi,\n options?.openapi\n );\n\n const nestHostApp = express();\n if (options?.accessiOptions?.http?.trustProxy !== undefined) {\n app.set('trust proxy', options.accessiOptions.http.trustProxy);\n nestHostApp.set('trust proxy', options.accessiOptions.http.trustProxy);\n }\n const jwksPath = options?.accessiOptions\n ? getAccessiJwksPath(options.accessiOptions)\n : undefined;\n const isNestRoute = (path: string) =>\n path === \"/api\" ||\n path.startsWith(\"/api/\") ||\n isOpenApiRequestPath(path, resolvedOpenApi) ||\n (jwksPath !== undefined && path === jwksPath);\n const edgeRateLimit = options?.accessiOptions?.edgeRateLimit\n ? createAccessiEdgeRateLimitMiddleware({\n ...options.accessiOptions.edgeRateLimit,\n match: options.accessiOptions.edgeRateLimit.match ?? ((path) => isNestRoute(path)),\n })\n : undefined;\n if (edgeRateLimit) {\n app.use(edgeRateLimit);\n }\n if (options?.accessiOptions && options.accessiOptions.http?.securityHeaders !== false) {\n const securityHeaders = createAccessiSecurityHeadersMiddleware(\n options.accessiOptions.http?.securityHeaders,\n isNestRoute,\n { isOpenApiUi: (path) => isOpenApiRequestPath(path, resolvedOpenApi) },\n );\n if (securityHeaders) {\n app.use(securityHeaders);\n }\n }\n\n app.use((req: Request, res, next) => {\n if (!isNestRoute(req.path)) {\n return next();\n }\n return nestHostApp(req, res, next);\n });\n\n if (options?.accessiOptions) {\n setupAccessiJwks(app, options.accessiOptions);\n if (jwksPath) {\n logger.info(`JWKS accessi disponibile su: ${jwksPath}`);\n }\n }\n\n const nestExpressInstance = new ExpressAdapter(nestHostApp);\n const nestApp = await NestFactory.create(EmilsoftwareModule.forRoot(options), nestExpressInstance, {\n bufferLogs: options?.bufferLogs !== false,\n ...(options?.logger ? { logger: options.logger } : {})\n });\n\n if (options?.accessiOptions?.http?.cors !== false) {\n nestApp.enableCors(options?.accessiOptions?.http?.cors);\n }\n if (options?.accessiOptions) {\n // Keep the Accessi API contract identical for standalone and unified bootstraps.\n nestApp.useGlobalPipes(createAccessiValidationPipe());\n nestApp.useGlobalFilters(new AccessiHttpExceptionFilter());\n }\n\n nestApp.setGlobalPrefix('api', {\n exclude: buildOpenApiExcludePaths(resolvedOpenApi)\n });\n\n // Documentazione OpenAPI del modulo unificato (Accessi + Allegati).\n const document = setupOpenApi(nestApp, resolvedOpenApi);\n\n await nestApp.init();\n if (options?.accessiOptions) {\n const accessiServices = registerAccessiModuleServices(app, nestApp);\n app.locals.accessiAuthService = accessiServices.authenticateGenService;\n setAccessiAuthService(accessiServices.authenticateGenService);\n logAccessiStartup(options.accessiOptions, resolvedOpenApi);\n }\n\n logger.info(\"Emilsoftware module initialized successfully\");\n\n return { nestApp, document };\n } catch (error) {\n if (options?.accessiOptions) {\n failAccessiAuthInitialization(error);\n }\n logger.error(\"Error initializing Emilsoftware Module:\", error);\n throw error;\n }\n}\n"]}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# Header di sicurezza e CSP
|
|
2
|
+
|
|
3
|
+
Accessi applica header di sicurezza alle proprie rotte (`/api/accessi/*`, JWKS e
|
|
4
|
+
documentazione OpenAPI). La configurazione avviene in `http.securityHeaders`:
|
|
5
|
+
|
|
6
|
+
```ts
|
|
7
|
+
const accessiOptions: AccessiOptions = {
|
|
8
|
+
// ...
|
|
9
|
+
http: {
|
|
10
|
+
securityHeaders: {
|
|
11
|
+
frameOptions: 'SAMEORIGIN',
|
|
12
|
+
referrerPolicy: 'no-referrer',
|
|
13
|
+
permissionsPolicy: 'camera=(), microphone=(), geolocation=()',
|
|
14
|
+
cacheControl: 'no-store, max-age=0',
|
|
15
|
+
contentSecurityPolicy: {
|
|
16
|
+
reportOnly: false,
|
|
17
|
+
connectSrc: ['https://api.example.com'],
|
|
18
|
+
reportUri: 'https://csp.example.com/report',
|
|
19
|
+
directives: {
|
|
20
|
+
'img-src': ["'self'", 'data:', 'https:'],
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
`securityHeaders: false` disabilita tutto; `securityHeaders: true` (o l'omissione)
|
|
29
|
+
usa i default. La forma storica booleana resta valida.
|
|
30
|
+
|
|
31
|
+
## Default
|
|
32
|
+
|
|
33
|
+
| Header | Valore | Note |
|
|
34
|
+
| --- | --- | --- |
|
|
35
|
+
| `X-Content-Type-Options` | `nosniff` | sempre presente |
|
|
36
|
+
| `X-Frame-Options` | `SAMEORIGIN` | `false` per non inviarlo |
|
|
37
|
+
| `Referrer-Policy` | `no-referrer` | `false` per non inviarla |
|
|
38
|
+
| `Permissions-Policy` | `camera=(), microphone=(), geolocation=()` | `false` per non inviarla |
|
|
39
|
+
| `Cache-Control` (shell console) | `no-store, max-age=0` | `false` per non inviarlo |
|
|
40
|
+
| `Content-Security-Policy` | vedi sotto | `false` per disabilitarla |
|
|
41
|
+
|
|
42
|
+
La shell della console risponde sempre con `Cache-Control: no-store`,
|
|
43
|
+
`Referrer-Policy: no-referrer` e `X-Content-Type-Options: nosniff`, anche con
|
|
44
|
+
`securityHeaders: false`, per non regredire rispetto alle versioni precedenti.
|
|
45
|
+
|
|
46
|
+
## Content Security Policy
|
|
47
|
+
|
|
48
|
+
Direttive di base:
|
|
49
|
+
|
|
50
|
+
```
|
|
51
|
+
default-src 'self'
|
|
52
|
+
base-uri 'none'
|
|
53
|
+
object-src 'none'
|
|
54
|
+
frame-ancestors 'self'
|
|
55
|
+
form-action 'self'
|
|
56
|
+
img-src 'self' data:
|
|
57
|
+
font-src 'self'
|
|
58
|
+
style-src 'self'
|
|
59
|
+
script-src 'self'
|
|
60
|
+
connect-src 'self'
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
La policy cambia per superficie:
|
|
64
|
+
|
|
65
|
+
- **Console** (`/api/accessi/console*`): i due script inline della shell ricevono
|
|
66
|
+
un nonce casuale per richiesta (`script-src 'self' 'nonce-...'`). Non viene mai
|
|
67
|
+
usato `'unsafe-inline'`. Se la console chiama l'API su un'altra origine, quella
|
|
68
|
+
origine viene aggiunta automaticamente a `connect-src` da
|
|
69
|
+
`consoleApiBaseUrl`.
|
|
70
|
+
- **OpenAPI** (`/accessi/openapi*`): la UI Swagger genera script e stili inline a
|
|
71
|
+
runtime, quindi la policy include `'unsafe-inline'` per `script-src` e
|
|
72
|
+
`style-src`. È l'unica eccezione documentata; per rimuoverla impostare
|
|
73
|
+
`contentSecurityPolicy.swaggerUnsafeInline: false` e servire una UI Swagger
|
|
74
|
+
compatibile con nonce/hash.
|
|
75
|
+
- **API e JWKS**: policy stretta, senza `'unsafe-inline'`.
|
|
76
|
+
|
|
77
|
+
`directives` vince sui default: una direttiva impostata a `false` viene rimossa,
|
|
78
|
+
altrimenti i valori sostituiscono quelli predefiniti. `connectSrc` aggiunge
|
|
79
|
+
origini a `connect-src`; `reportUri` aggiunge `report-uri`.
|
|
80
|
+
|
|
81
|
+
### Rollout con Report-Only
|
|
82
|
+
|
|
83
|
+
1. Imposta `contentSecurityPolicy.reportOnly: true` e un `reportUri`.
|
|
84
|
+
2. Raccogli le violazioni per almeno un ciclo di utilizzo della console.
|
|
85
|
+
3. Aggiungi le origini legittime a `connectSrc` o alle direttive.
|
|
86
|
+
4. Passa a `reportOnly: false`.
|
|
87
|
+
|
|
88
|
+
## Reverse proxy
|
|
89
|
+
|
|
90
|
+
Accessi non imposta HSTS: va configurato sul terminatore TLS insieme ai
|
|
91
|
+
redirect HTTP→HTTPS.
|
|
92
|
+
|
|
93
|
+
### Nginx
|
|
94
|
+
|
|
95
|
+
```nginx
|
|
96
|
+
location / {
|
|
97
|
+
proxy_pass http://accessi_upstream;
|
|
98
|
+
proxy_set_header Host $host;
|
|
99
|
+
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
100
|
+
proxy_set_header X-Forwarded-Proto $scheme;
|
|
101
|
+
|
|
102
|
+
# Non rimuovere gli header CSP inviati da Accessi.
|
|
103
|
+
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
`add_header` di Nginx si aggiunge solo se la risposta non ha già un header con
|
|
108
|
+
lo stesso nome: non duplicare `Content-Security-Policy` a questo livello. Se il
|
|
109
|
+
proxy deve imporre una policy propria, disattiva quella di Accessi
|
|
110
|
+
(`contentSecurityPolicy: false`) e mantieni una sola policy, consapevole che la
|
|
111
|
+
console richiede il nonce (il proxy non può generarlo al posto dell'app).
|
|
112
|
+
|
|
113
|
+
### Traefik
|
|
114
|
+
|
|
115
|
+
```yaml
|
|
116
|
+
http:
|
|
117
|
+
middlewares:
|
|
118
|
+
accessi-headers:
|
|
119
|
+
headers:
|
|
120
|
+
stsSeconds: 31536000
|
|
121
|
+
stsIncludeSubdomains: true
|
|
122
|
+
contentTypeNosniff: true
|
|
123
|
+
referrerPolicy: no-referrer
|
|
124
|
+
customResponseHeaders:
|
|
125
|
+
Permissions-Policy: "camera=(), microphone=(), geolocation=()"
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Non aggiungere `Content-Security-Policy` in `customResponseHeaders` se Accessi la
|
|
129
|
+
invia già: due header CSP vengono applicati come intersezione e la console
|
|
130
|
+
smetterebbe di funzionare. Per una policy unica, gestiscila in un solo punto.
|
|
131
|
+
|
|
132
|
+
### Cloudflare
|
|
133
|
+
|
|
134
|
+
- Abilita "Always Use HTTPS" e HSTS nelle impostazioni TLS/Edge.
|
|
135
|
+
- Se usi Transform Rules per aggiungere header, escludi
|
|
136
|
+
`Content-Security-Policy`: Cloudflare non può generare il nonce della console.
|
|
137
|
+
- Verifica che "Email Obfuscation" o altre feature che iniettano script inline
|
|
138
|
+
siano disattivate sulle pagine della console, altrimenti violano la CSP.
|
|
139
|
+
- Con Cloudflare davanti alla JWKS, mantieni il TTL della cache allineato a
|
|
140
|
+
`jwt.jwks.cacheTtlMs` (default 300 s): un TTL più alto ritarda la rotazione
|
|
141
|
+
delle chiavi.
|
|
142
|
+
|
|
143
|
+
## Verifica
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
curl -sSI https://accessi.example.com/api/accessi/console | grep -i -E 'content-security|x-frame|referrer|permissions'
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
La CSP della console cambia a ogni risposta per via del nonce: due richieste
|
|
150
|
+
consecutive devono restituire policy diverse. Il nonce nell'header deve
|
|
151
|
+
coincidere con quello dei due script inline nella pagina.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "emilsoftware-utilities",
|
|
3
|
-
"version": "2.0.0-dev.
|
|
3
|
+
"version": "2.0.0-dev.76",
|
|
4
4
|
"description": "Utilities for EmilSoftware",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.js",
|
|
@@ -139,8 +139,8 @@
|
|
|
139
139
|
"typecheck": "tsc --noEmit",
|
|
140
140
|
"lint": "node scripts/lint.cjs",
|
|
141
141
|
"test": "npm run typecheck && npm run lint && npm run test:unit",
|
|
142
|
-
"test:unit": "node --test test/angular-components.cjs test/menus-catalog.mjs test/menu-scopes.cjs test/accessi-regressions.cjs test/password-reset-regressions.cjs test/two-factor.cjs test/accessi-schema.cjs test/utilities-regressions.cjs test/service-token-regressions.cjs test/accessi-email-config.cjs test/accessi-catalog-scripts.cjs test/openapi-config.cjs test/resource-server.cjs test/accessi-security-standards.cjs test/accessi-service.cjs test/refresh-token.cjs test/token-revocation.cjs test/accessi-client.cjs test/accessi-client-scopes.cjs test/openapi-semantic-diff.cjs test/proxy-ip.cjs test/readiness.cjs test/jwt-key-rotation.cjs",
|
|
143
|
-
"test:coverage": "node --experimental-test-coverage --test-coverage-lines=50 --test-coverage-functions=60 --test-coverage-branches=60 --test test/angular-components.cjs test/menus-catalog.mjs test/menu-scopes.cjs test/accessi-regressions.cjs test/password-reset-regressions.cjs test/two-factor.cjs test/accessi-schema.cjs test/utilities-regressions.cjs test/service-token-regressions.cjs test/accessi-email-config.cjs test/accessi-catalog-scripts.cjs test/openapi-config.cjs test/resource-server.cjs test/accessi-security-standards.cjs test/accessi-service.cjs test/refresh-token.cjs test/token-revocation.cjs test/accessi-client.cjs test/accessi-client-scopes.cjs test/openapi-semantic-diff.cjs test/proxy-ip.cjs test/readiness.cjs test/jwt-key-rotation.cjs",
|
|
142
|
+
"test:unit": "node --test test/angular-components.cjs test/menus-catalog.mjs test/menu-scopes.cjs test/accessi-regressions.cjs test/password-reset-regressions.cjs test/two-factor.cjs test/accessi-schema.cjs test/utilities-regressions.cjs test/service-token-regressions.cjs test/accessi-email-config.cjs test/accessi-catalog-scripts.cjs test/openapi-config.cjs test/resource-server.cjs test/accessi-security-standards.cjs test/accessi-service.cjs test/refresh-token.cjs test/token-revocation.cjs test/accessi-client.cjs test/accessi-client-scopes.cjs test/openapi-semantic-diff.cjs test/proxy-ip.cjs test/readiness.cjs test/jwt-key-rotation.cjs test/security-headers.cjs",
|
|
143
|
+
"test:coverage": "node --experimental-test-coverage --test-coverage-lines=50 --test-coverage-functions=60 --test-coverage-branches=60 --test test/angular-components.cjs test/menus-catalog.mjs test/menu-scopes.cjs test/accessi-regressions.cjs test/password-reset-regressions.cjs test/two-factor.cjs test/accessi-schema.cjs test/utilities-regressions.cjs test/service-token-regressions.cjs test/accessi-email-config.cjs test/accessi-catalog-scripts.cjs test/openapi-config.cjs test/resource-server.cjs test/accessi-security-standards.cjs test/accessi-service.cjs test/refresh-token.cjs test/token-revocation.cjs test/accessi-client.cjs test/accessi-client-scopes.cjs test/openapi-semantic-diff.cjs test/proxy-ip.cjs test/readiness.cjs test/jwt-key-rotation.cjs test/security-headers.cjs",
|
|
144
144
|
"test:accessi": "npm run test:unit",
|
|
145
145
|
"test:all": "npm test && npm run test:integration && npm run test:integration:redis",
|
|
146
146
|
"test:integration": "node test/integration/run-firebird.cjs 5",
|
|
Binary file
|