mirror of
https://github.com/sasjs/server.git
synced 2025-12-10 19:34:34 +00:00
Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
25dc5dd215 | ||
|
|
503994dbd2 | ||
|
|
0dceb5c3c3 | ||
|
|
1af04fa3b3 | ||
|
|
efa81fec77 | ||
|
|
10caf1918a | ||
|
|
4ed20a3b75 | ||
|
|
98b2c5fa25 | ||
|
|
3ad327b85f | ||
|
|
dd3acce393 |
@@ -2,6 +2,13 @@
|
|||||||
|
|
||||||
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
||||||
|
|
||||||
|
### [0.0.70](https://github.com/sasjs/server/compare/v0.0.69...v0.0.70) (2022-05-06)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* CSP_DISABLE env option ([dd3acce](https://github.com/sasjs/server/commit/dd3acce3935e7cfc0b2c44a401314306915a3a10))
|
||||||
|
|
||||||
### [0.0.69](https://github.com/sasjs/server/compare/v0.0.68...v0.0.69) (2022-05-02)
|
### [0.0.69](https://github.com/sasjs/server/compare/v0.0.68...v0.0.69) (2022-05-02)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
72
README.md
72
README.md
@@ -48,15 +48,22 @@ When launching the app, it will make use of specific environment variables. Thes
|
|||||||
Example contents of a `.env` file:
|
Example contents of a `.env` file:
|
||||||
|
|
||||||
```
|
```
|
||||||
# options: [desktop|server] default: `desktop`
|
#
|
||||||
|
## Core Settings
|
||||||
|
#
|
||||||
|
|
||||||
|
|
||||||
|
# MODE options: [desktop|server] default: `desktop`
|
||||||
|
# Desktop mode is single user and designed for workstation use
|
||||||
|
# Server mode is multi-user and suitable for intranet / internet use
|
||||||
MODE=
|
MODE=
|
||||||
|
|
||||||
# options: [disable|enable] default: `disable` for `server` & `enable` for `desktop`
|
# Path to SAS executable (sas.exe / sas.sh)
|
||||||
# If enabled, be sure to also configure the WHITELIST of third party servers.
|
SAS_PATH=/path/to/sas/executable.exe
|
||||||
CORS=
|
|
||||||
|
|
||||||
# options: <http://localhost:3000 https://abc.com ...> space separated urls
|
# Path to working directory
|
||||||
WHITELIST=
|
# This location is for SAS WORK, staged files, DRIVE, configuration etc
|
||||||
|
DRIVE_PATH=/tmp
|
||||||
|
|
||||||
# options: [http|https] default: http
|
# options: [http|https] default: http
|
||||||
PROTOCOL=
|
PROTOCOL=
|
||||||
@@ -65,16 +72,22 @@ PROTOCOL=
|
|||||||
PORT=
|
PORT=
|
||||||
|
|
||||||
|
|
||||||
# optional
|
#
|
||||||
# for MODE: `desktop`, prompts user
|
## Additional SAS Options
|
||||||
# for MODE: `server` gets value from api/package.json `configuration.sasPath`
|
#
|
||||||
SAS_PATH=/path/to/sas/executable.exe
|
|
||||||
|
|
||||||
|
|
||||||
# optional
|
# On windows use SAS_OPTIONS and on unix use SASV9_OPTIONS
|
||||||
# for MODE: `desktop`, prompts user
|
# Any options set here are automatically applied in the SAS session
|
||||||
# for MODE: `server` defaults to /tmp
|
# See: https://documentation.sas.com/doc/en/pgmsascdc/9.4_3.5/hostunx/p0wrdmqp8k0oyyn1xbx3bp3qy2wl.htm
|
||||||
DRIVE_PATH=/tmp
|
# And: https://documentation.sas.com/doc/en/pgmsascdc/9.4_3.5/hostwin/p0drw76qo0gig2n1kcoliekh605k.htm#p09y7hx0grw1gin1giuvrjyx61m6
|
||||||
|
SAS_OPTIONS= -NOXCMD
|
||||||
|
SASV9_OPTIONS= -NOXCMD
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
## Additional Web Server Options
|
||||||
|
#
|
||||||
|
|
||||||
# ENV variables required for PROTOCOL: `https`
|
# ENV variables required for PROTOCOL: `https`
|
||||||
PRIVATE_KEY=privkey.pem
|
PRIVATE_KEY=privkey.pem
|
||||||
@@ -87,13 +100,30 @@ AUTH_CODE_SECRET=<secret>
|
|||||||
SESSION_SECRET=<secret>
|
SESSION_SECRET=<secret>
|
||||||
DB_CONNECT=mongodb+srv://<DB_USERNAME>:<DB_PASSWORD>@<CLUSTER>/<DB_NAME>?retryWrites=true&w=majority
|
DB_CONNECT=mongodb+srv://<DB_USERNAME>:<DB_PASSWORD>@<CLUSTER>/<DB_NAME>?retryWrites=true&w=majority
|
||||||
|
|
||||||
# SAS Options
|
# options: [disable|enable] default: `disable` for `server` & `enable` for `desktop`
|
||||||
# On windows use SAS_OPTIONS and on unix use SASV9_OPTIONS
|
# If enabled, be sure to also configure the WHITELIST of third party servers.
|
||||||
# Any options set here are automatically applied in the SAS session
|
CORS=
|
||||||
# See: https://documentation.sas.com/doc/en/pgmsascdc/9.4_3.5/hostunx/p0wrdmqp8k0oyyn1xbx3bp3qy2wl.htm
|
|
||||||
# And: https://documentation.sas.com/doc/en/pgmsascdc/9.4_3.5/hostwin/p0drw76qo0gig2n1kcoliekh605k.htm#p09y7hx0grw1gin1giuvrjyx61m6
|
# options: <http://localhost:3000 https://abc.com ...> space separated urls
|
||||||
SAS_OPTIONS= -NOXCMD
|
WHITELIST=
|
||||||
SASV9_OPTIONS= -NOXCMD
|
|
||||||
|
# HELMET Cross Origin Embedder Policy
|
||||||
|
# Sets the Cross-Origin-Embedder-Policy header to require-corp when `true`
|
||||||
|
# options: [true|false] default: true
|
||||||
|
# Docs: https://helmetjs.github.io/#reference (`crossOriginEmbedderPolicy`)
|
||||||
|
HELMET_COEP=
|
||||||
|
|
||||||
|
# HELMET Content Security Policy
|
||||||
|
# Path to a json file containing HELMET `contentSecurityPolicy` directives
|
||||||
|
# Docs: https://helmetjs.github.io/#reference
|
||||||
|
#
|
||||||
|
# Example config:
|
||||||
|
# {
|
||||||
|
# "img-src": ["'self'", "domain.com"],
|
||||||
|
# "script-src": ["'self'", "'unsafe-inline'"],
|
||||||
|
# "script-src-attr": ["'self'", "'unsafe-inline'"]
|
||||||
|
# }
|
||||||
|
HELMET_CSP_CONFIG_PATH=./csp.config.json
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,9 @@ FULL_CHAIN=fullchain.pem
|
|||||||
|
|
||||||
PORT=[5000] default value is 5000
|
PORT=[5000] default value is 5000
|
||||||
|
|
||||||
|
HELMET_CSP_CONFIG_PATH=./csp.config.json if omitted HELMET default will be used
|
||||||
|
HELMET_COEP=[true|false] if omitted HELMET default will be used
|
||||||
|
|
||||||
ACCESS_TOKEN_SECRET=<secret>
|
ACCESS_TOKEN_SECRET=<secret>
|
||||||
REFRESH_TOKEN_SECRET=<secret>
|
REFRESH_TOKEN_SECRET=<secret>
|
||||||
AUTH_CODE_SECRET=<secret>
|
AUTH_CODE_SECRET=<secret>
|
||||||
|
|||||||
5
api/csp.config.example.json
Normal file
5
api/csp.config.example.json
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"img-src": ["'self'", "domen.com"],
|
||||||
|
"script-src": ["'self'", "'unsafe-inline'"],
|
||||||
|
"script-src-attr": ["'self'", "'unsafe-inline'"]
|
||||||
|
}
|
||||||
@@ -17,6 +17,7 @@ import {
|
|||||||
setProcessVariables,
|
setProcessVariables,
|
||||||
setupFolders
|
setupFolders
|
||||||
} from './utils'
|
} from './utils'
|
||||||
|
import { getEnvCSPDirectives } from './utils/parseHelmetConfig'
|
||||||
|
|
||||||
dotenv.config()
|
dotenv.config()
|
||||||
|
|
||||||
@@ -25,7 +26,8 @@ const app = express()
|
|||||||
app.use(cookieParser())
|
app.use(cookieParser())
|
||||||
app.use(morgan('tiny'))
|
app.use(morgan('tiny'))
|
||||||
|
|
||||||
const { MODE, CORS, WHITELIST, PROTOCOL } = process.env
|
const { MODE, CORS, WHITELIST, PROTOCOL, HELMET_CSP_CONFIG_PATH, HELMET_COEP } =
|
||||||
|
process.env
|
||||||
|
|
||||||
export const cookieOptions = {
|
export const cookieOptions = {
|
||||||
secure: PROTOCOL === 'https',
|
secure: PROTOCOL === 'https',
|
||||||
@@ -33,6 +35,10 @@ export const cookieOptions = {
|
|||||||
maxAge: 24 * 60 * 60 * 1000 // 24 hours
|
maxAge: 24 * 60 * 60 * 1000 // 24 hours
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const cspConfigJson = getEnvCSPDirectives(HELMET_CSP_CONFIG_PATH)
|
||||||
|
const coepFlag =
|
||||||
|
HELMET_COEP === 'true' || HELMET_COEP === undefined ? true : false
|
||||||
|
|
||||||
/***********************************
|
/***********************************
|
||||||
* CSRF Protection *
|
* CSRF Protection *
|
||||||
***********************************/
|
***********************************/
|
||||||
@@ -46,9 +52,10 @@ app.use(
|
|||||||
contentSecurityPolicy: {
|
contentSecurityPolicy: {
|
||||||
directives: {
|
directives: {
|
||||||
...helmet.contentSecurityPolicy.getDefaultDirectives(),
|
...helmet.contentSecurityPolicy.getDefaultDirectives(),
|
||||||
'script-src': ["'self'", "'unsafe-inline'"]
|
...cspConfigJson
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
crossOriginEmbedderPolicy: coepFlag
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
33
api/src/utils/parseHelmetConfig.ts
Normal file
33
api/src/utils/parseHelmetConfig.ts
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
import path from 'path'
|
||||||
|
import fs from 'fs'
|
||||||
|
|
||||||
|
export const getEnvCSPDirectives = (
|
||||||
|
HELMET_CSP_CONFIG_PATH: string | undefined
|
||||||
|
) => {
|
||||||
|
let cspConfigJson = {
|
||||||
|
'script-src': ["'self'", "'unsafe-inline'"]
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
typeof HELMET_CSP_CONFIG_PATH === 'string' &&
|
||||||
|
HELMET_CSP_CONFIG_PATH.length > 0
|
||||||
|
) {
|
||||||
|
const cspConfigPath = path.join(process.cwd(), HELMET_CSP_CONFIG_PATH)
|
||||||
|
|
||||||
|
try {
|
||||||
|
let file = fs.readFileSync(cspConfigPath).toString()
|
||||||
|
|
||||||
|
try {
|
||||||
|
cspConfigJson = JSON.parse(file)
|
||||||
|
} catch (e) {
|
||||||
|
console.error(
|
||||||
|
'Parsing Content Security Policy JSON config failed. Make sure it is valid json'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Error reading HELMET CSP config file', e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return cspConfigJson
|
||||||
|
}
|
||||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "server",
|
"name": "server",
|
||||||
"version": "0.0.69",
|
"version": "0.0.70",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "server",
|
"name": "server",
|
||||||
"version": "0.0.69",
|
"version": "0.0.70",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"prettier": "^2.3.1",
|
"prettier": "^2.3.1",
|
||||||
"standard-version": "^9.3.2"
|
"standard-version": "^9.3.2"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "server",
|
"name": "server",
|
||||||
"version": "0.0.69",
|
"version": "0.0.70",
|
||||||
"description": "NodeJS wrapper for calling the SAS binary executable",
|
"description": "NodeJS wrapper for calling the SAS binary executable",
|
||||||
"repository": "https://github.com/sasjs/server",
|
"repository": "https://github.com/sasjs/server",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
7
web/package-lock.json
generated
7
web/package-lock.json
generated
@@ -21,6 +21,7 @@
|
|||||||
"@types/node": "^12.20.28",
|
"@types/node": "^12.20.28",
|
||||||
"@types/react": "^17.0.27",
|
"@types/react": "^17.0.27",
|
||||||
"axios": "^0.24.0",
|
"axios": "^0.24.0",
|
||||||
|
"monaco-editor": "^0.33.0",
|
||||||
"monaco-editor-webpack-plugin": "^7.0.1",
|
"monaco-editor-webpack-plugin": "^7.0.1",
|
||||||
"react": "^17.0.2",
|
"react": "^17.0.2",
|
||||||
"react-dom": "^17.0.2",
|
"react-dom": "^17.0.2",
|
||||||
@@ -8422,8 +8423,7 @@
|
|||||||
"node_modules/monaco-editor": {
|
"node_modules/monaco-editor": {
|
||||||
"version": "0.33.0",
|
"version": "0.33.0",
|
||||||
"resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.33.0.tgz",
|
"resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.33.0.tgz",
|
||||||
"integrity": "sha512-VcRWPSLIUEgQJQIE0pVT8FcGBIgFoxz7jtqctE+IiCxWugD0DwgyQBcZBhdSrdMC84eumoqMZsGl2GTreOzwqw==",
|
"integrity": "sha512-VcRWPSLIUEgQJQIE0pVT8FcGBIgFoxz7jtqctE+IiCxWugD0DwgyQBcZBhdSrdMC84eumoqMZsGl2GTreOzwqw=="
|
||||||
"peer": true
|
|
||||||
},
|
},
|
||||||
"node_modules/monaco-editor-webpack-plugin": {
|
"node_modules/monaco-editor-webpack-plugin": {
|
||||||
"version": "7.0.1",
|
"version": "7.0.1",
|
||||||
@@ -17505,8 +17505,7 @@
|
|||||||
"monaco-editor": {
|
"monaco-editor": {
|
||||||
"version": "0.33.0",
|
"version": "0.33.0",
|
||||||
"resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.33.0.tgz",
|
"resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.33.0.tgz",
|
||||||
"integrity": "sha512-VcRWPSLIUEgQJQIE0pVT8FcGBIgFoxz7jtqctE+IiCxWugD0DwgyQBcZBhdSrdMC84eumoqMZsGl2GTreOzwqw==",
|
"integrity": "sha512-VcRWPSLIUEgQJQIE0pVT8FcGBIgFoxz7jtqctE+IiCxWugD0DwgyQBcZBhdSrdMC84eumoqMZsGl2GTreOzwqw=="
|
||||||
"peer": true
|
|
||||||
},
|
},
|
||||||
"monaco-editor-webpack-plugin": {
|
"monaco-editor-webpack-plugin": {
|
||||||
"version": "7.0.1",
|
"version": "7.0.1",
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
"@types/node": "^12.20.28",
|
"@types/node": "^12.20.28",
|
||||||
"@types/react": "^17.0.27",
|
"@types/react": "^17.0.27",
|
||||||
"axios": "^0.24.0",
|
"axios": "^0.24.0",
|
||||||
|
"monaco-editor": "^0.33.0",
|
||||||
"monaco-editor-webpack-plugin": "^7.0.1",
|
"monaco-editor-webpack-plugin": "^7.0.1",
|
||||||
"react": "^17.0.2",
|
"react": "^17.0.2",
|
||||||
"react-dom": "^17.0.2",
|
"react-dom": "^17.0.2",
|
||||||
|
|||||||
Reference in New Issue
Block a user