Skip to content

Commit 1e5d090

Browse files
dav-istimneutkens
authored andcommitted
Block Certain Env Keys That Are Used Internally (vercel#6260)
Closes: vercel#6244 This will block the following keys: ``` NODE_.+ __.+ ``` There doesn't seem to be a way to simulate a failed build or else I'd add tests for it.
1 parent d2ef344 commit 1e5d090

File tree

12 files changed

+83
-23
lines changed

12 files changed

+83
-23
lines changed

errors/env-key-not-allowed.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# The key "<your key>" under "env" in next.config.js is not allowed.
2+
3+
#### Why This Error Occurred
4+
5+
Next.js configures internal variables for replacement itself. These start with `__` or `NODE_`, for this reason they are not allowed as values for `env` in `next.config.js`
6+
7+
#### Possible Ways to Fix It
8+
9+
Rename the specified key so that it does not start with `__` or `NODE_`.

packages/next-server/server/config.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {CONFIG_FILE} from 'next-server/constants'
44
const targets = ['server', 'serverless']
55

66
const defaultConfig = {
7+
env: [],
78
webpack: null,
89
webpackDevMiddleware: null,
910
poweredByHeader: true,

packages/next-server/server/next-server.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -249,7 +249,7 @@ export default class Server {
249249
}
250250

251251
if (this.nextConfig.poweredByHeader) {
252-
res.setHeader('X-Powered-By', 'Next.js ' + process.env.NEXT_VERSION)
252+
res.setHeader('X-Powered-By', 'Next.js ' + process.env.__NEXT_VERSION)
253253
}
254254
return this.sendHTML(req, res, html)
255255
}

packages/next-server/taskfile-typescript.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ try {
3535
}
3636

3737
// update file's data
38-
file.data = Buffer.from(result.outputText.replace(/process\.env\.NEXT_VERSION/, `"${require('./package.json').version}"`), 'utf8')
38+
file.data = Buffer.from(result.outputText.replace(/process\.env\.__NEXT_VERSION/, `"${require('./package.json').version}"`), 'utf8')
3939
})
4040
}
4141
} catch (err) {

packages/next/bin/next.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ const args = arg({
3737
// Version is inlined into the file using taskr build pipeline
3838
if (args['--version']) {
3939
// tslint:disable-next-line
40-
console.log(`Next.js v${process.env.NEXT_VERSION}`)
40+
console.log(`Next.js v${process.env.__NEXT_VERSION}`)
4141
process.exit(0)
4242
}
4343

packages/next/build/webpack-config.js

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -291,18 +291,20 @@ export default async function getBaseWebpackConfig (dir, {dev = false, isServer
291291
dev && new CaseSensitivePathPlugin(), // Since on macOS the filesystem is case-insensitive this will make sure your path are case-sensitive
292292
!dev && new webpack.HashedModuleIdsPlugin(),
293293
// Removes server/client code by minifier
294-
new webpack.DefinePlugin(Object.assign(
295-
{},
296-
config.env ? Object.keys(config.env)
297-
.reduce((acc, key) => ({
294+
new webpack.DefinePlugin({
295+
...(Object.keys(config.env).reduce((acc, key) => {
296+
if (/^(?:NODE_.+)|(?:__.+)$/i.test(key)) {
297+
throw new Error(`The key "${key}" under "env" in next.config.js is not allowed. https://err.sh/zeit/next.js/env-key-not-allowed`)
298+
}
299+
300+
return {
298301
...acc,
299-
...{ [`process.env.${key}`]: JSON.stringify(config.env[key]) }
300-
}), {}) : {},
301-
{
302-
'process.crossOrigin': JSON.stringify(config.crossOrigin),
303-
'process.browser': JSON.stringify(!isServer)
304-
}
305-
)),
302+
[`process.env.${key}`]: JSON.stringify(config.env[key])
303+
}
304+
}, {})),
305+
'process.crossOrigin': JSON.stringify(config.crossOrigin),
306+
'process.browser': JSON.stringify(!isServer)
307+
}),
306308
// This is used in client/dev-error-overlay/hot-dev-client.js to replace the dist directory
307309
!isServer && dev && new webpack.DefinePlugin({
308310
'process.env.__NEXT_DIST_DIR': JSON.stringify(distDir)

packages/next/client/on-demand-entries-client.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ export default async ({ assetPrefix }) => {
2020
}
2121

2222
return new Promise(resolve => {
23-
ws = new WebSocket(`${wsProtocol}://${hostname}:${process.env.NEXT_WS_PORT}${process.env.NEXT_WS_PROXY_PATH}`)
23+
ws = new WebSocket(`${wsProtocol}://${hostname}:${process.env.__NEXT_WS_PORT}${process.env.__NEXT_WS_PROXY_PATH}`)
2424
ws.onopen = () => resolve()
2525
ws.onclose = () => {
2626
setTimeout(async () => {

packages/next/server/hot-reloader.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -167,8 +167,8 @@ export default class HotReloader {
167167
addWsConfig (configs) {
168168
const { websocketProxyPath, websocketProxyPort } = this.config.onDemandEntries
169169
const opts = {
170-
'process.env.NEXT_WS_PORT': websocketProxyPort || this.wsPort,
171-
'process.env.NEXT_WS_PROXY_PATH': JSON.stringify(websocketProxyPath)
170+
'process.env.__NEXT_WS_PORT': websocketProxyPort || this.wsPort,
171+
'process.env.__NEXT_WS_PROXY_PATH': JSON.stringify(websocketProxyPath)
172172
}
173173
configs[0].plugins.push(new webpack.DefinePlugin(opts))
174174
}

packages/next/taskfile-typescript.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ try {
4040
if (file.base === 'next-dev.js') result.outputText = result.outputText.replace('// REPLACE_NOOP_IMPORT', `import('./noop');`)
4141

4242
// update file's data
43-
file.data = Buffer.from(result.outputText.replace(/process\.env\.NEXT_VERSION/, `"${require('./package.json').version}"`), 'utf8')
43+
file.data = Buffer.from(result.outputText.replace(/process\.env\.__NEXT_VERSION/, `"${require('./package.json').version}"`), 'utf8')
4444
})
4545
}
4646
} catch (err) {

test/integration/production-config/next.config.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,19 @@ const withCSS = require('@zeit/next-css')
22
const withSass = require('@zeit/next-sass')
33
const path = require('path')
44
module.exports = withCSS(withSass({
5+
env: {
6+
...(process.env.ENABLE_ENV_FAIL_UNDERSCORE ? {
7+
'__NEXT_MY_VAR': 'test'
8+
} : {}),
9+
...(process.env.ENABLE_ENV_FAIL_NODE ? {
10+
'NODE_ENV': 'abc'
11+
} : {})
12+
},
513
onDemandEntries: {
614
// Make sure entries are not getting disposed.
715
maxInactiveAge: 1000 * 60 * 60
816
},
9-
webpack (config, {buildId}) {
17+
webpack (config) {
1018
// When next-css is `npm link`ed we have to solve loaders from the project root
1119
const nextLocation = path.join(require.resolve('next/package.json'), '../')
1220
const nextCssNodeModulesLocation = path.join(

test/integration/production-config/test/index.test.js

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,20 @@ import {
55
nextServer,
66
nextBuild,
77
startApp,
8-
stopApp
8+
stopApp,
9+
runNextCommand
910
} from 'next-test-utils'
1011
import webdriver from 'next-webdriver'
1112

1213
jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000 * 60 * 5
1314

15+
const appDir = join(__dirname, '../')
16+
1417
let appPort
1518
let server
1619

1720
describe('Production Config Usage', () => {
1821
beforeAll(async () => {
19-
const appDir = join(__dirname, '../')
2022
await nextBuild(appDir)
2123
const app = nextServer({
2224
dir: join(__dirname, '../'),
@@ -37,6 +39,34 @@ describe('Production Config Usage', () => {
3739
})
3840
})
3941

42+
describe('env', () => {
43+
it('should fail with __ in env key', async () => {
44+
const result = await runNextCommand(['build', appDir], {spawnOptions: {
45+
env: {
46+
...process.env,
47+
ENABLE_ENV_FAIL_UNDERSCORE: true
48+
}
49+
},
50+
stdout: true,
51+
stderr: true})
52+
53+
expect(result.stderr).toMatch(/The key "__NEXT_MY_VAR" under/)
54+
})
55+
56+
it('should fail with NODE_ in env key', async () => {
57+
const result = await runNextCommand(['build', appDir], {spawnOptions: {
58+
env: {
59+
...process.env,
60+
ENABLE_ENV_FAIL_NODE: true
61+
}
62+
},
63+
stdout: true,
64+
stderr: true})
65+
66+
expect(result.stderr).toMatch(/The key "NODE_ENV" under/)
67+
})
68+
})
69+
4070
describe('with generateBuildId', () => {
4171
it('should add the custom buildid', async () => {
4272
const browser = await webdriver(appPort, '/')

test/lib/next-test-utils.js

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,14 @@ export function runNextCommand (argv, options = {}) {
6969
const cwd = path.dirname(require.resolve('next/package'))
7070
return new Promise((resolve, reject) => {
7171
console.log(`Running command "next ${argv.join(' ')}"`)
72-
const instance = spawn('node', ['dist/bin/next', ...argv], { cwd, stdio: options.stdout ? ['ignore', 'pipe', 'ignore'] : 'inherit' })
72+
const instance = spawn('node', ['dist/bin/next', ...argv], { ...options.spawnOptions, cwd, stdio: ['ignore', 'pipe', 'pipe'] })
73+
74+
let stderrOutput = ''
75+
if (options.stderr) {
76+
instance.stderr.on('data', function (chunk) {
77+
stderrOutput += chunk
78+
})
79+
}
7380

7481
let stdoutOutput = ''
7582
if (options.stdout) {
@@ -80,11 +87,14 @@ export function runNextCommand (argv, options = {}) {
8087

8188
instance.on('close', () => {
8289
resolve({
83-
stdout: stdoutOutput
90+
stdout: stdoutOutput,
91+
stderr: stderrOutput
8492
})
8593
})
8694

8795
instance.on('error', (err) => {
96+
err.stdout = stdoutOutput
97+
err.stderr = stderrOutput
8898
reject(err)
8999
})
90100
})

0 commit comments

Comments
 (0)