commandmate 0.2.12 → 0.3.0
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/.env.example +21 -2
- package/.next/BUILD_ID +1 -1
- package/.next/app-build-manifest.json +30 -22
- package/.next/app-path-routes-manifest.json +1 -1
- package/.next/build-manifest.json +7 -7
- package/.next/cache/.tsbuildinfo +1 -1
- package/.next/cache/config.json +3 -3
- package/.next/cache/webpack/client-production/0.pack +0 -0
- package/.next/cache/webpack/client-production/1.pack +0 -0
- package/.next/cache/webpack/client-production/2.pack +0 -0
- package/.next/cache/webpack/client-production/index.pack +0 -0
- package/.next/cache/webpack/client-production/index.pack.old +0 -0
- package/.next/cache/webpack/edge-server-production/0.pack +0 -0
- package/.next/cache/webpack/edge-server-production/index.pack +0 -0
- package/.next/cache/webpack/server-production/0.pack +0 -0
- package/.next/cache/webpack/server-production/index.pack +0 -0
- package/.next/next-server.js.nft.json +1 -1
- package/.next/prerender-manifest.json +1 -1
- package/.next/react-loadable-manifest.json +2 -2
- package/.next/required-server-files.json +1 -1
- package/.next/routes-manifest.json +1 -1
- package/.next/server/app/_not-found/page.js +1 -1
- package/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/server/app/api/app/update-check/route.js +1 -1
- package/.next/server/app/api/auth/login/route.js +1 -0
- package/.next/server/app/api/auth/login/route.js.nft.json +1 -0
- package/.next/server/app/api/auth/logout/route.js +1 -0
- package/.next/server/app/api/auth/logout/route.js.nft.json +1 -0
- package/.next/server/app/api/auth/status/route.js +1 -0
- package/.next/server/app/api/auth/status/route.js.nft.json +1 -0
- package/.next/server/app/api/hooks/claude-done/route.js +1 -1
- package/.next/server/app/api/hooks/claude-done/route.js.nft.json +1 -1
- package/.next/server/app/api/repositories/route.js +2 -2
- package/.next/server/app/api/repositories/route.js.nft.json +1 -1
- package/.next/server/app/api/slash-commands/route.js +1 -1
- package/.next/server/app/api/slash-commands/route.js.nft.json +1 -1
- package/.next/server/app/api/slash-commands.body +1 -1
- package/.next/server/app/api/worktrees/[id]/auto-yes/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/auto-yes/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/current-output/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/current-output/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/interrupt/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/interrupt/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/kill-session/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/kill-session/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/logs/[filename]/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/prompt-response/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/prompt-response/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/respond/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/respond/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/send/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/send/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/slash-commands/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/slash-commands/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/start-polling/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/start-polling/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/route.js +1 -1
- package/.next/server/app/api/worktrees/route.js.nft.json +1 -1
- package/.next/server/app/login/page.js +1 -0
- package/.next/server/app/login/page.js.nft.json +1 -0
- package/.next/server/app/login/page_client-reference-manifest.js +1 -0
- package/.next/server/app/page.js +2 -2
- package/.next/server/app/page.js.nft.json +1 -1
- package/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/server/app/worktrees/[id]/files/[...path]/page.js +1 -1
- package/.next/server/app/worktrees/[id]/files/[...path]/page.js.nft.json +1 -1
- package/.next/server/app/worktrees/[id]/files/[...path]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/worktrees/[id]/page.js +3 -3
- package/.next/server/app/worktrees/[id]/page.js.nft.json +1 -1
- package/.next/server/app/worktrees/[id]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/worktrees/[id]/terminal/page.js +1 -1
- package/.next/server/app/worktrees/[id]/terminal/page.js.nft.json +1 -1
- package/.next/server/app/worktrees/[id]/terminal/page_client-reference-manifest.js +1 -1
- package/.next/server/app-paths-manifest.json +13 -9
- package/.next/server/chunks/3013.js +1 -0
- package/.next/server/chunks/3074.js +1 -0
- package/.next/server/chunks/{1287.js → 3294.js} +2 -2
- package/.next/server/chunks/3860.js +1 -1
- package/.next/server/chunks/4893.js +2 -2
- package/.next/server/chunks/539.js +35 -0
- package/.next/server/chunks/5795.js +1 -0
- package/.next/server/chunks/7536.js +1 -1
- package/.next/server/chunks/7566.js +19 -0
- package/.next/server/chunks/8693.js +1 -1
- package/.next/server/edge-runtime-webpack.js +2 -0
- package/.next/server/edge-runtime-webpack.js.map +1 -0
- package/.next/server/functions-config-manifest.json +1 -1
- package/.next/server/middleware-build-manifest.js +1 -1
- package/.next/server/middleware-manifest.json +28 -2
- package/.next/server/middleware-react-loadable-manifest.js +1 -1
- package/.next/server/pages/500.html +1 -1
- package/.next/server/server-reference-manifest.json +1 -1
- package/.next/server/src/middleware.js +14 -0
- package/.next/server/src/middleware.js.map +1 -0
- package/.next/static/chunks/{2626.2125083a1ff3b80a.js → 6163.f672451d4575decf.js} +1 -1
- package/.next/static/chunks/{656.d72f25ce819bd77e.js → 656.5e2de0173f5a06bd.js} +1 -1
- package/.next/static/chunks/8091-925542bdfc843dce.js +1 -0
- package/.next/static/chunks/8528-4d554d3b94d4cf9b.js +1 -0
- package/.next/static/chunks/app/{layout-07755491d5d57242.js → layout-9110f9a5e41c6bf4.js} +1 -1
- package/.next/static/chunks/app/login/page-2d42204ba87cd136.js +1 -0
- package/.next/static/chunks/app/page-238b5a70d8c101e9.js +1 -0
- package/.next/static/chunks/app/worktrees/[id]/page-9418e49bdc1de02c.js +1 -0
- package/.next/static/chunks/main-db79434ee4a6c931.js +1 -0
- package/.next/static/chunks/webpack-3c0ee3ce5b546818.js +1 -0
- package/.next/static/css/b9ea6a4fad17dc32.css +3 -0
- package/.next/trace +5 -5
- package/.next/types/app/api/auth/login/route.ts +343 -0
- package/.next/types/app/api/auth/logout/route.ts +343 -0
- package/.next/types/app/api/auth/status/route.ts +343 -0
- package/.next/types/app/login/page.ts +79 -0
- package/README.md +6 -1
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +2 -0
- package/dist/cli/commands/start.d.ts +2 -0
- package/dist/cli/commands/start.d.ts.map +1 -1
- package/dist/cli/commands/start.js +159 -14
- package/dist/cli/commands/status.d.ts.map +1 -1
- package/dist/cli/commands/status.js +4 -0
- package/dist/cli/config/security-messages.d.ts +3 -1
- package/dist/cli/config/security-messages.d.ts.map +1 -1
- package/dist/cli/config/security-messages.js +6 -2
- package/dist/cli/index.js +17 -0
- package/dist/cli/types/index.d.ts +17 -0
- package/dist/cli/types/index.d.ts.map +1 -1
- package/dist/cli/utils/daemon.d.ts.map +1 -1
- package/dist/cli/utils/daemon.js +16 -3
- package/dist/config/auth-config.d.ts +43 -0
- package/dist/config/auth-config.d.ts.map +1 -0
- package/dist/config/auth-config.js +112 -0
- package/dist/lib/auth.d.ts +104 -0
- package/dist/lib/auth.d.ts.map +1 -0
- package/dist/lib/auth.js +250 -0
- package/dist/server/server.js +123 -12
- package/dist/server/src/config/auth-config.js +112 -0
- package/dist/server/src/lib/auth.js +250 -0
- package/dist/server/src/lib/auto-yes-manager.js +180 -96
- package/dist/server/src/lib/ip-restriction.js +241 -0
- package/dist/server/src/lib/response-poller.js +92 -39
- package/dist/server/src/lib/ws-server.js +63 -33
- package/dist/server/src/types/slash-commands.js +1 -0
- package/package.json +2 -2
- package/.next/server/chunks/9238.js +0 -35
- package/.next/server/chunks/9367.js +0 -19
- package/.next/static/chunks/5970-2e18108d0cabd8af.js +0 -1
- package/.next/static/chunks/816-af44cb865b0c980e.js +0 -1
- package/.next/static/chunks/app/page-a6593b9640df66a6.js +0 -1
- package/.next/static/chunks/app/worktrees/[id]/page-c99258f57461962c.js +0 -1
- package/.next/static/chunks/main-f00f82f1cf18dd99.js +0 -1
- package/.next/static/chunks/webpack-af8567a485ade35a.js +0 -1
- package/.next/static/css/897ffb669f47c97b.css +0 -3
- /package/.next/static/{ym6mA6Dl9wX62h3AoYO45 → clTo9tuAoPMLcGRuVENfO}/_buildManifest.js +0 -0
- /package/.next/static/{ym6mA6Dl9wX62h3AoYO45 → clTo9tuAoPMLcGRuVENfO}/_ssgManifest.js +0 -0
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* Issue #96: npm install CLI support
|
|
5
5
|
* Issue #125: Use getEnvPath and getPidFilePath for correct path resolution
|
|
6
6
|
* Issue #136: Add --issue and --auto-port flags for worktree support
|
|
7
|
+
* Issue #331: Add --auth, --auth-expire, --https, --cert, --key, --allow-http flags
|
|
7
8
|
* Start CommandMate server
|
|
8
9
|
*/
|
|
9
10
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
@@ -21,11 +22,41 @@ const security_messages_1 = require("../config/security-messages");
|
|
|
21
22
|
const input_validators_1 = require("../utils/input-validators");
|
|
22
23
|
const port_allocator_1 = require("../utils/port-allocator");
|
|
23
24
|
const resource_resolvers_1 = require("../utils/resource-resolvers");
|
|
25
|
+
const auth_1 = require("../../lib/auth");
|
|
24
26
|
const logger = new logger_1.CLILogger();
|
|
27
|
+
/**
|
|
28
|
+
* Display the generated authentication token to the user.
|
|
29
|
+
* Called once after server start; the token is not persisted and cannot be retrieved later.
|
|
30
|
+
*
|
|
31
|
+
* @param token - The plaintext authentication token to display
|
|
32
|
+
*/
|
|
33
|
+
function displayAuthToken(token) {
|
|
34
|
+
logger.info('');
|
|
35
|
+
logger.info('Authentication token (save this - it will not be shown again):');
|
|
36
|
+
logger.info(` ${token}`);
|
|
37
|
+
logger.info('');
|
|
38
|
+
}
|
|
39
|
+
/** HTTPS certificate warning when --auth is used without --cert/--key */
|
|
40
|
+
const HTTPS_WARNING = `
|
|
41
|
+
\x1b[1m\x1b[33mWARNING: Authentication enabled without HTTPS\x1b[0m
|
|
42
|
+
|
|
43
|
+
Token will be transmitted in plain text over HTTP.
|
|
44
|
+
For secure authentication, use HTTPS with a TLS certificate.
|
|
45
|
+
|
|
46
|
+
\x1b[1mQuick setup with mkcert:\x1b[0m
|
|
47
|
+
brew install mkcert # macOS
|
|
48
|
+
mkcert -install
|
|
49
|
+
mkcert localhost
|
|
50
|
+
|
|
51
|
+
commandmate start --auth --cert ./localhost.pem --key ./localhost-key.pem
|
|
52
|
+
|
|
53
|
+
Use --allow-http to suppress this warning.
|
|
54
|
+
`;
|
|
25
55
|
/**
|
|
26
56
|
* Execute start command
|
|
27
57
|
* Issue #125: Use getEnvPath and getPidFilePath for correct path resolution
|
|
28
58
|
* Issue #136: Support --issue and --auto-port flags for worktree servers
|
|
59
|
+
* Issue #331: Support --auth, --auth-expire, --https, --cert, --key, --allow-http
|
|
29
60
|
*/
|
|
30
61
|
async function startCommand(options) {
|
|
31
62
|
try {
|
|
@@ -38,6 +69,17 @@ async function startCommand(options) {
|
|
|
38
69
|
return;
|
|
39
70
|
}
|
|
40
71
|
}
|
|
72
|
+
// Issue #331: Validate auth-expire format before proceeding
|
|
73
|
+
if (options.authExpire) {
|
|
74
|
+
try {
|
|
75
|
+
(0, auth_1.parseDuration)(options.authExpire);
|
|
76
|
+
}
|
|
77
|
+
catch (error) {
|
|
78
|
+
logger.error(`Invalid --auth-expire value: ${(0, types_1.getErrorMessage)(error)}`);
|
|
79
|
+
process.exit(types_1.ExitCode.CONFIG_ERROR);
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
41
83
|
// Issue #125: Check for .env file at correct location
|
|
42
84
|
// Issue #136: Use issue number for worktree-specific paths
|
|
43
85
|
const envPath = (0, env_setup_1.getEnvPath)(options.issue);
|
|
@@ -69,6 +111,58 @@ async function startCommand(options) {
|
|
|
69
111
|
dbPath = dbResolver.resolve(options.issue);
|
|
70
112
|
logger.info(`Using database: ${dbPath}`);
|
|
71
113
|
}
|
|
114
|
+
// Issue #331: Generate token and hash if --auth is enabled
|
|
115
|
+
let authTokenHash;
|
|
116
|
+
let authToken;
|
|
117
|
+
if (options.auth) {
|
|
118
|
+
authToken = (0, auth_1.generateToken)();
|
|
119
|
+
authTokenHash = (0, auth_1.hashToken)(authToken);
|
|
120
|
+
}
|
|
121
|
+
// Issue #331: Validate --cert/--key must be specified together
|
|
122
|
+
if ((options.cert && !options.key) || (!options.cert && options.key)) {
|
|
123
|
+
logger.error('--cert and --key must be specified together');
|
|
124
|
+
process.exit(types_1.ExitCode.CONFIG_ERROR);
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
// Issue #331: Determine protocol (matches server.ts: cert+key → always HTTPS)
|
|
128
|
+
const hasCert = !!(options.cert && options.key);
|
|
129
|
+
const protocol = hasCert ? 'https' : 'http';
|
|
130
|
+
// Issue #331: HTTPS certificate validation
|
|
131
|
+
if (hasCert) {
|
|
132
|
+
if (!(0, fs_1.existsSync)(options.cert)) {
|
|
133
|
+
logger.error(`Certificate file not found: ${options.cert}`);
|
|
134
|
+
process.exit(types_1.ExitCode.CONFIG_ERROR);
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
if (!(0, fs_1.existsSync)(options.key)) {
|
|
138
|
+
logger.error(`Key file not found: ${options.key}`);
|
|
139
|
+
process.exit(types_1.ExitCode.CONFIG_ERROR);
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
// Issue #331: Warn about --auth without HTTPS
|
|
144
|
+
if (options.auth && !hasCert && !options.allowHttp) {
|
|
145
|
+
console.log(HTTPS_WARNING);
|
|
146
|
+
}
|
|
147
|
+
// Load .env to check for legacy CM_AUTH_TOKEN
|
|
148
|
+
const mainEnvResult = (0, dotenv_1.config)({ path: mainEnvPath });
|
|
149
|
+
let envResult = mainEnvResult;
|
|
150
|
+
if (options.issue !== undefined && (0, fs_1.existsSync)(envPath) && envPath !== mainEnvPath) {
|
|
151
|
+
envResult = (0, dotenv_1.config)({ path: envPath, override: true });
|
|
152
|
+
}
|
|
153
|
+
// Issue #331: Warn about legacy CM_AUTH_TOKEN
|
|
154
|
+
const allEnvParsed = {
|
|
155
|
+
...(mainEnvResult.parsed || {}),
|
|
156
|
+
...(envResult.parsed || {}),
|
|
157
|
+
};
|
|
158
|
+
if (allEnvParsed.CM_AUTH_TOKEN) {
|
|
159
|
+
logger.warn('CM_AUTH_TOKEN found in .env file. This is no longer used.');
|
|
160
|
+
logger.warn('Use "commandmate start --auth" instead. The token is now generated automatically.');
|
|
161
|
+
}
|
|
162
|
+
if (mainEnvResult.error) {
|
|
163
|
+
logger.warn(`Failed to load .env file at ${mainEnvPath}: ${mainEnvResult.error.message}`);
|
|
164
|
+
logger.info('Continuing with existing environment variables');
|
|
165
|
+
}
|
|
72
166
|
// Daemon mode
|
|
73
167
|
if (options.daemon) {
|
|
74
168
|
// Check if already running
|
|
@@ -79,23 +173,57 @@ async function startCommand(options) {
|
|
|
79
173
|
return;
|
|
80
174
|
}
|
|
81
175
|
logger.info(`Starting ${serverLabel} in background...`);
|
|
176
|
+
// Issue #331: Set auth environment variables in process.env so daemon.ts can forward them
|
|
177
|
+
// to the child process. daemon.ts reads process.env[key] to build the child env object.
|
|
178
|
+
if (authTokenHash) {
|
|
179
|
+
process.env.CM_AUTH_TOKEN_HASH = authTokenHash;
|
|
180
|
+
}
|
|
181
|
+
if (options.authExpire) {
|
|
182
|
+
process.env.CM_AUTH_EXPIRE = options.authExpire;
|
|
183
|
+
}
|
|
184
|
+
if (options.cert) {
|
|
185
|
+
process.env.CM_HTTPS_CERT = options.cert;
|
|
186
|
+
}
|
|
187
|
+
if (options.key) {
|
|
188
|
+
process.env.CM_HTTPS_KEY = options.key;
|
|
189
|
+
}
|
|
190
|
+
if (options.allowHttp) {
|
|
191
|
+
process.env.CM_ALLOW_HTTP = '1';
|
|
192
|
+
}
|
|
193
|
+
// Issue #332: Set IP restriction env vars for daemon mode
|
|
194
|
+
if (options.allowedIps) {
|
|
195
|
+
process.env.CM_ALLOWED_IPS = options.allowedIps;
|
|
196
|
+
}
|
|
197
|
+
if (options.trustProxy) {
|
|
198
|
+
process.env.CM_TRUST_PROXY = 'true';
|
|
199
|
+
}
|
|
82
200
|
try {
|
|
83
201
|
const pid = await daemonManager.start({
|
|
84
202
|
dev: options.dev,
|
|
85
203
|
port: port,
|
|
86
204
|
// Issue #136: Pass DB path for worktree servers
|
|
87
205
|
dbPath: dbPath,
|
|
206
|
+
// Issue #331: Pass auth and HTTPS options
|
|
207
|
+
auth: options.auth,
|
|
208
|
+
authExpire: options.authExpire,
|
|
209
|
+
cert: options.cert,
|
|
210
|
+
key: options.key,
|
|
211
|
+
allowHttp: options.allowHttp,
|
|
88
212
|
});
|
|
89
213
|
logger.success(`${serverLabel} started in background (PID: ${pid})`);
|
|
90
214
|
const actualPort = port || parseInt(process.env.CM_PORT || '3000', 10);
|
|
91
215
|
const bind = process.env.CM_BIND || '127.0.0.1';
|
|
92
|
-
const url =
|
|
216
|
+
const url = `${protocol}://${bind === '0.0.0.0' ? '127.0.0.1' : bind}:${actualPort}`;
|
|
93
217
|
logger.info(`URL: ${url}`);
|
|
218
|
+
// Issue #331: Show token to user (shown once, not persisted)
|
|
219
|
+
if (authToken) {
|
|
220
|
+
displayAuthToken(authToken);
|
|
221
|
+
}
|
|
94
222
|
(0, security_logger_1.logSecurityEvent)({
|
|
95
223
|
timestamp: new Date().toISOString(),
|
|
96
224
|
command: 'start',
|
|
97
225
|
action: 'success',
|
|
98
|
-
details: `Daemon started (PID: ${pid})${options.issue !== undefined ? ` (Issue #${options.issue})` : ''}`,
|
|
226
|
+
details: `Daemon started (PID: ${pid})${options.issue !== undefined ? ` (Issue #${options.issue})` : ''}${options.auth ? ' [auth enabled]' : ''}`,
|
|
99
227
|
});
|
|
100
228
|
process.exit(types_1.ExitCode.SUCCESS);
|
|
101
229
|
}
|
|
@@ -115,17 +243,6 @@ async function startCommand(options) {
|
|
|
115
243
|
// Foreground mode (default)
|
|
116
244
|
const npmScript = options.dev ? 'dev' : 'start';
|
|
117
245
|
logger.info(`Starting ${serverLabel} in foreground (${options.dev ? 'development' : 'production'} mode)...`);
|
|
118
|
-
// Issue #125: Load .env file from correct location (same as daemon mode)
|
|
119
|
-
// Issue #136: Load main .env first, then overlay worktree-specific .env if exists
|
|
120
|
-
const mainEnvResult = (0, dotenv_1.config)({ path: mainEnvPath });
|
|
121
|
-
let envResult = mainEnvResult;
|
|
122
|
-
if (options.issue !== undefined && (0, fs_1.existsSync)(envPath) && envPath !== mainEnvPath) {
|
|
123
|
-
envResult = (0, dotenv_1.config)({ path: envPath, override: true });
|
|
124
|
-
}
|
|
125
|
-
if (mainEnvResult.error) {
|
|
126
|
-
logger.warn(`Failed to load .env file at ${mainEnvPath}: ${mainEnvResult.error.message}`);
|
|
127
|
-
logger.info('Continuing with existing environment variables');
|
|
128
|
-
}
|
|
129
246
|
// Build environment by merging process.env with .env values
|
|
130
247
|
const env = {
|
|
131
248
|
...process.env,
|
|
@@ -140,11 +257,39 @@ async function startCommand(options) {
|
|
|
140
257
|
if (dbPath) {
|
|
141
258
|
env.CM_DB_PATH = dbPath;
|
|
142
259
|
}
|
|
260
|
+
// Issue #331: Set auth environment variables
|
|
261
|
+
if (authTokenHash) {
|
|
262
|
+
env.CM_AUTH_TOKEN_HASH = authTokenHash;
|
|
263
|
+
}
|
|
264
|
+
if (options.authExpire) {
|
|
265
|
+
env.CM_AUTH_EXPIRE = options.authExpire;
|
|
266
|
+
}
|
|
267
|
+
if (options.cert) {
|
|
268
|
+
env.CM_HTTPS_CERT = options.cert;
|
|
269
|
+
}
|
|
270
|
+
if (options.key) {
|
|
271
|
+
env.CM_HTTPS_KEY = options.key;
|
|
272
|
+
}
|
|
273
|
+
if (options.allowHttp) {
|
|
274
|
+
env.CM_ALLOW_HTTP = '1';
|
|
275
|
+
}
|
|
276
|
+
// Issue #332: Set IP restriction env vars for foreground mode
|
|
277
|
+
if (options.allowedIps) {
|
|
278
|
+
env.CM_ALLOWED_IPS = options.allowedIps;
|
|
279
|
+
}
|
|
280
|
+
if (options.trustProxy) {
|
|
281
|
+
env.CM_TRUST_PROXY = 'true';
|
|
282
|
+
}
|
|
143
283
|
// Issue #179: Security warning for external access - recommend reverse proxy
|
|
144
284
|
const bindAddress = env.CM_BIND || '127.0.0.1';
|
|
145
|
-
|
|
285
|
+
// Issue #332: Suppress warning when --allowed-ips is set (IP restriction provides access control)
|
|
286
|
+
if (bindAddress === '0.0.0.0' && !options.auth && !options.allowedIps) {
|
|
146
287
|
console.log(security_messages_1.REVERSE_PROXY_WARNING);
|
|
147
288
|
}
|
|
289
|
+
// Issue #331: Show token to user (foreground mode, shown once, not persisted)
|
|
290
|
+
if (authToken) {
|
|
291
|
+
displayAuthToken(authToken);
|
|
292
|
+
}
|
|
148
293
|
// Use package installation directory, not current working directory
|
|
149
294
|
const packageRoot = (0, paths_1.getPackageRoot)();
|
|
150
295
|
const child = (0, child_process_1.spawn)('npm', ['run', npmScript], {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/status.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,EAA6B,aAAa,EAAE,MAAM,UAAU,CAAC;AAqFpE;;;;GAIG;AACH,wBAAsB,aAAa,CAAC,OAAO,GAAE,aAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/status.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,EAA6B,aAAa,EAAE,MAAM,UAAU,CAAC;AAqFpE;;;;GAIG;AACH,wBAAsB,aAAa,CAAC,OAAO,GAAE,aAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAkF9E"}
|
|
@@ -140,6 +140,10 @@ async function statusCommand(options = {}) {
|
|
|
140
140
|
if (status.url) {
|
|
141
141
|
console.log(`URL: ${status.url}`);
|
|
142
142
|
}
|
|
143
|
+
// Issue #332: Show IP restriction status
|
|
144
|
+
if (process.env.CM_ALLOWED_IPS) {
|
|
145
|
+
console.log(`IP ACL: ${process.env.CM_ALLOWED_IPS}`);
|
|
146
|
+
}
|
|
143
147
|
console.log('');
|
|
144
148
|
process.exit(types_1.ExitCode.SUCCESS);
|
|
145
149
|
}
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Security Messages
|
|
3
3
|
* Issue #179: Reverse proxy authentication recommendation
|
|
4
|
+
* Issue #331: Updated to include --auth option
|
|
4
5
|
* Shared warning constants for CLI commands (DRY principle)
|
|
5
6
|
*
|
|
6
7
|
* [SF-002] GitHub URLs imported from github-links.ts (DRY)
|
|
7
8
|
*/
|
|
8
9
|
/**
|
|
9
|
-
* Warning message displayed when CM_BIND=0.0.0.0
|
|
10
|
+
* Warning message displayed when CM_BIND=0.0.0.0 and --auth is not enabled
|
|
10
11
|
* Used by: init.ts, start.ts, daemon.ts
|
|
12
|
+
* Issue #331: Added --auth as a recommended option
|
|
11
13
|
*/
|
|
12
14
|
export declare const REVERSE_PROXY_WARNING: string;
|
|
13
15
|
//# sourceMappingURL=security-messages.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"security-messages.d.ts","sourceRoot":"","sources":["../../../src/cli/config/security-messages.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"security-messages.d.ts","sourceRoot":"","sources":["../../../src/cli/config/security-messages.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,QAkBjC,CAAC"}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Security Messages
|
|
4
4
|
* Issue #179: Reverse proxy authentication recommendation
|
|
5
|
+
* Issue #331: Updated to include --auth option
|
|
5
6
|
* Shared warning constants for CLI commands (DRY principle)
|
|
6
7
|
*
|
|
7
8
|
* [SF-002] GitHub URLs imported from github-links.ts (DRY)
|
|
@@ -10,20 +11,23 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
10
11
|
exports.REVERSE_PROXY_WARNING = void 0;
|
|
11
12
|
const github_links_1 = require("../../config/github-links");
|
|
12
13
|
/**
|
|
13
|
-
* Warning message displayed when CM_BIND=0.0.0.0
|
|
14
|
+
* Warning message displayed when CM_BIND=0.0.0.0 and --auth is not enabled
|
|
14
15
|
* Used by: init.ts, start.ts, daemon.ts
|
|
16
|
+
* Issue #331: Added --auth as a recommended option
|
|
15
17
|
*/
|
|
16
18
|
exports.REVERSE_PROXY_WARNING = `
|
|
17
19
|
\x1b[1m\x1b[31mWARNING: Server is exposed to external networks without authentication\x1b[0m
|
|
18
20
|
|
|
19
21
|
Exposing the server without reverse proxy authentication
|
|
20
|
-
is a serious security risk.
|
|
22
|
+
or built-in token auth is a serious security risk.
|
|
21
23
|
|
|
22
24
|
\x1b[1mRisks:\x1b[0m
|
|
23
25
|
File read/write/delete and command execution
|
|
24
26
|
become accessible to third parties.
|
|
25
27
|
|
|
26
28
|
\x1b[1mRecommended authentication methods:\x1b[0m
|
|
29
|
+
- commandmate start --auth (built-in token auth)
|
|
30
|
+
- commandmate start --allowed-ips 192.168.1.0/24 (IP restriction)
|
|
27
31
|
- Nginx + Basic Auth
|
|
28
32
|
- Cloudflare Access
|
|
29
33
|
- Tailscale
|
package/dist/cli/index.js
CHANGED
|
@@ -33,6 +33,7 @@ program
|
|
|
33
33
|
});
|
|
34
34
|
// Start command
|
|
35
35
|
// Issue #136: Add --issue and --auto-port flags for worktree support
|
|
36
|
+
// Issue #331: Add --auth, --auth-expire, --https, --cert, --key, --allow-http flags
|
|
36
37
|
program
|
|
37
38
|
.command('start')
|
|
38
39
|
.description('Start the CommandMate server')
|
|
@@ -41,6 +42,14 @@ program
|
|
|
41
42
|
.option('-p, --port <number>', 'Override port number', parseInt)
|
|
42
43
|
.option('-i, --issue <number>', 'Start server for specific issue worktree', parseInt)
|
|
43
44
|
.option('--auto-port', 'Automatically allocate port for worktree server')
|
|
45
|
+
.option('--auth', 'Enable token authentication')
|
|
46
|
+
.option('--auth-expire <duration>', 'Token expiration (e.g., 24h, 7d, 90m)')
|
|
47
|
+
.option('--https', 'Enable HTTPS')
|
|
48
|
+
.option('--cert <path>', 'Path to TLS certificate file')
|
|
49
|
+
.option('--key <path>', 'Path to TLS private key file')
|
|
50
|
+
.option('--allow-http', 'Suppress HTTPS warning when using --auth without certificates')
|
|
51
|
+
.option('--allowed-ips <cidrs>', 'Allowed IP addresses/CIDR ranges (comma-separated)')
|
|
52
|
+
.option('--trust-proxy', 'Trust X-Forwarded-For header from reverse proxy')
|
|
44
53
|
.action(async (options) => {
|
|
45
54
|
await (0, start_1.startCommand)({
|
|
46
55
|
dev: options.dev,
|
|
@@ -48,6 +57,14 @@ program
|
|
|
48
57
|
port: options.port,
|
|
49
58
|
issue: options.issue,
|
|
50
59
|
autoPort: options.autoPort,
|
|
60
|
+
auth: options.auth,
|
|
61
|
+
authExpire: options.authExpire,
|
|
62
|
+
https: options.https,
|
|
63
|
+
cert: options.cert,
|
|
64
|
+
key: options.key,
|
|
65
|
+
allowHttp: options.allowHttp,
|
|
66
|
+
allowedIps: options.allowedIps,
|
|
67
|
+
trustProxy: options.trustProxy,
|
|
51
68
|
});
|
|
52
69
|
});
|
|
53
70
|
// Stop command
|
|
@@ -26,6 +26,7 @@ export interface InitOptions {
|
|
|
26
26
|
/**
|
|
27
27
|
* Options for start command
|
|
28
28
|
* Issue #136: Added issue and autoPort for worktree support
|
|
29
|
+
* Issue #331: Added auth, authExpire, https, cert, key, allowHttp for token auth and HTTPS
|
|
29
30
|
*/
|
|
30
31
|
export interface StartOptions {
|
|
31
32
|
/** Start in development mode */
|
|
@@ -40,6 +41,22 @@ export interface StartOptions {
|
|
|
40
41
|
autoPort?: boolean;
|
|
41
42
|
/** Override database path for worktree server (Issue #136) */
|
|
42
43
|
dbPath?: string;
|
|
44
|
+
/** Enable token authentication (Issue #331) */
|
|
45
|
+
auth?: boolean;
|
|
46
|
+
/** Token expiration duration (e.g., "24h", "7d") (Issue #331) */
|
|
47
|
+
authExpire?: string;
|
|
48
|
+
/** Enable HTTPS (Issue #331) */
|
|
49
|
+
https?: boolean;
|
|
50
|
+
/** Path to TLS certificate file (Issue #331) */
|
|
51
|
+
cert?: string;
|
|
52
|
+
/** Path to TLS private key file (Issue #331) */
|
|
53
|
+
key?: string;
|
|
54
|
+
/** Suppress HTTPS warning when using --auth without certificates (Issue #331) */
|
|
55
|
+
allowHttp?: boolean;
|
|
56
|
+
/** Issue #332: Allowed IP addresses/CIDR ranges (comma-separated) */
|
|
57
|
+
allowedIps?: string;
|
|
58
|
+
/** Issue #332: Trust X-Forwarded-For header from reverse proxy */
|
|
59
|
+
trustProxy?: boolean;
|
|
43
60
|
}
|
|
44
61
|
/**
|
|
45
62
|
* Options for stop command
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/cli/types/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;GAGG;AACH,oBAAY,QAAQ;IAClB,OAAO,IAAI;IACX,gBAAgB,IAAI;IACpB,YAAY,IAAI;IAChB,YAAY,IAAI;IAChB,WAAW,IAAI;IACf,gBAAgB,KAAK;CACtB;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,2CAA2C;IAC3C,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,uCAAuC;IACvC,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/cli/types/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;GAGG;AACH,oBAAY,QAAQ;IAClB,OAAO,IAAI;IACX,gBAAgB,IAAI;IACpB,YAAY,IAAI;IAChB,YAAY,IAAI;IAChB,WAAW,IAAI;IACf,gBAAgB,KAAK;CACtB;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,2CAA2C;IAC3C,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,uCAAuC;IACvC,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED;;;;GAIG;AACH,MAAM,WAAW,YAAY;IAC3B,gCAAgC;IAChC,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,wBAAwB;IACxB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,2BAA2B;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,6DAA6D;IAC7D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,4DAA4D;IAC5D,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,8DAA8D;IAC9D,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,+CAA+C;IAC/C,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,iEAAiE;IACjE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gCAAgC;IAChC,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,gDAAgD;IAChD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,gDAAgD;IAChD,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,iFAAiF;IACjF,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,qEAAqE;IACrE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,kEAAkE;IAClE,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC1B,2BAA2B;IAC3B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,6DAA6D;IAC7D,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC5B,6DAA6D;IAC7D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,uDAAuD;IACvD,GAAG,CAAC,EAAE,OAAO,CAAC;CACf;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,oCAAoC;IACpC,OAAO,EAAE,OAAO,CAAC;IACjB,8BAA8B;IAC9B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,+BAA+B;IAC/B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,qCAAqC;IACrC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,4CAA4C;IAC5C,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC9B,mBAAmB;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,uBAAuB;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,8BAA8B;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,0CAA0C;IAC1C,QAAQ,EAAE,OAAO,CAAC;IAClB,iCAAiC;IACjC,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,sBAAsB;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,mBAAmB;IACnB,MAAM,EAAE,IAAI,GAAG,SAAS,GAAG,kBAAkB,CAAC;IAC9C,sCAAsC;IACtC,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,sDAAsD;IACtD,OAAO,EAAE,OAAO,CAAC;IACjB,oCAAoC;IACpC,OAAO,EAAE,gBAAgB,EAAE,CAAC;CAC7B;AAED;;;GAGG;AACH,MAAM,WAAW,SAAS;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;CACvB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,oCAAoC;IACpC,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,kDAAkD;IAClD,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC5B,0CAA0C;IAC1C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,mEAAmE;IACnE,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;CAC7C;AAED;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,gEAAgE;IAChE,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;;;;GAKG;AACH,MAAM,WAAW,kBAAkB;IACjC,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC1B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,OAAO,CAAC;CACf;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAKtD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"daemon.d.ts","sourceRoot":"","sources":["../../../src/cli/utils/daemon.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAOtD;;GAEG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,UAAU,CAAa;IAC/B,OAAO,CAAC,MAAM,CAAY;gBAEd,WAAW,EAAE,MAAM;IAK/B;;;;;OAKG;IACG,KAAK,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"daemon.d.ts","sourceRoot":"","sources":["../../../src/cli/utils/daemon.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAOtD;;GAEG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,UAAU,CAAa;IAC/B,OAAO,CAAC,MAAM,CAAY;gBAEd,WAAW,EAAE,MAAM;IAK/B;;;;;OAKG;IACG,KAAK,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC;IA6FnD;;;;OAIG;IACG,IAAI,CAAC,KAAK,GAAE,OAAe,GAAG,OAAO,CAAC,OAAO,CAAC;IA8BpD;;;OAGG;IACG,SAAS,IAAI,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC;IA+B/C;;OAEG;IACG,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC;IAInC;;OAEG;YACW,WAAW;CAiB1B"}
|
package/dist/cli/utils/daemon.js
CHANGED
|
@@ -64,14 +64,25 @@ class DaemonManager {
|
|
|
64
64
|
if (options.dbPath) {
|
|
65
65
|
env.CM_DB_PATH = options.dbPath;
|
|
66
66
|
}
|
|
67
|
+
// Issue #331: Forward auth and HTTPS environment variables from parent process to daemon.
|
|
68
|
+
// These are set by startCommand before calling daemon.start().
|
|
69
|
+
// Issue #332: Added CM_ALLOWED_IPS, CM_TRUST_PROXY for IP restriction in daemon mode
|
|
70
|
+
const authEnvKeys = ['CM_AUTH_TOKEN_HASH', 'CM_AUTH_EXPIRE', 'CM_HTTPS_CERT', 'CM_HTTPS_KEY', 'CM_ALLOW_HTTP', 'CM_ALLOWED_IPS', 'CM_TRUST_PROXY'];
|
|
71
|
+
for (const key of authEnvKeys) {
|
|
72
|
+
if (process.env[key]) {
|
|
73
|
+
env[key] = process.env[key];
|
|
74
|
+
}
|
|
75
|
+
}
|
|
67
76
|
// Issue #179: Security warning for external access - recommend reverse proxy
|
|
68
77
|
const bindAddress = env.CM_BIND || '127.0.0.1';
|
|
69
78
|
const port = env.CM_PORT || '3000';
|
|
70
|
-
|
|
79
|
+
const protocol = env.CM_HTTPS_CERT ? 'https' : 'http';
|
|
80
|
+
// Issue #332: Suppress warning when CM_ALLOWED_IPS is set (IP restriction provides access control)
|
|
81
|
+
if (bindAddress === '0.0.0.0' && !env.CM_AUTH_TOKEN_HASH && !env.CM_ALLOWED_IPS) {
|
|
71
82
|
console.log(security_messages_1.REVERSE_PROXY_WARNING);
|
|
72
83
|
}
|
|
73
84
|
// Log startup with accurate settings (Stage 4 review: MF-2)
|
|
74
|
-
this.logger.info(`Starting server at
|
|
85
|
+
this.logger.info(`Starting server at ${protocol}://${bindAddress}:${port}`);
|
|
75
86
|
// Spawn detached process
|
|
76
87
|
const child = (0, child_process_1.spawn)('npm', ['run', npmScript], {
|
|
77
88
|
cwd: packageRoot,
|
|
@@ -139,7 +150,9 @@ class DaemonManager {
|
|
|
139
150
|
// Get port from environment or default
|
|
140
151
|
const port = parseInt(process.env.CM_PORT || '3000', 10);
|
|
141
152
|
const bind = process.env.CM_BIND || '127.0.0.1';
|
|
142
|
-
|
|
153
|
+
// Issue #331: Use HTTPS protocol if certificate is configured
|
|
154
|
+
const protocol = process.env.CM_HTTPS_CERT ? 'https' : 'http';
|
|
155
|
+
const url = `${protocol}://${bind === '0.0.0.0' ? '127.0.0.1' : bind}:${port}`;
|
|
143
156
|
// Note: Getting accurate uptime would require storing start time
|
|
144
157
|
// For now, we don't track uptime
|
|
145
158
|
return {
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Authentication Configuration Constants
|
|
3
|
+
* Issue #331: Shared constants for auth.ts and middleware.ts
|
|
4
|
+
*
|
|
5
|
+
* CONSTRAINT: This module must be Edge Runtime compatible.
|
|
6
|
+
* No Node.js-specific imports (crypto, fs, etc.) are allowed.
|
|
7
|
+
*/
|
|
8
|
+
/** Cookie name for authentication token */
|
|
9
|
+
export declare const AUTH_COOKIE_NAME: "cm_auth_token";
|
|
10
|
+
/**
|
|
11
|
+
* Validate that a CM_AUTH_TOKEN_HASH value is a well-formed SHA-256 hex string.
|
|
12
|
+
* Used by both auth.ts and middleware.ts to ensure consistent auth-enabled detection.
|
|
13
|
+
* Returns a type predicate so callers can narrow the type after checking.
|
|
14
|
+
*
|
|
15
|
+
* @param hash - The hash string to validate
|
|
16
|
+
* @returns true if the hash is a valid 64-character hex string
|
|
17
|
+
*/
|
|
18
|
+
export declare function isValidTokenHash(hash: string | undefined): hash is string;
|
|
19
|
+
/**
|
|
20
|
+
* Paths excluded from authentication check.
|
|
21
|
+
* S002: Must use === for matching (no startsWith - bypass attack prevention)
|
|
22
|
+
*/
|
|
23
|
+
export declare const AUTH_EXCLUDED_PATHS: readonly ["/login", "/api/auth/login", "/api/auth/logout", "/api/auth/status"];
|
|
24
|
+
/** Default token expiration duration (24 hours) */
|
|
25
|
+
export declare const DEFAULT_EXPIRE_DURATION_MS: number;
|
|
26
|
+
/**
|
|
27
|
+
* Parse a duration string into milliseconds.
|
|
28
|
+
* Supported formats: Nh (hours), Nd (days), Nm (minutes)
|
|
29
|
+
* Minimum: 1h, Maximum: 30d
|
|
30
|
+
*
|
|
31
|
+
* @param s - Duration string (e.g., "24h", "7d", "90m")
|
|
32
|
+
* @returns Duration in milliseconds
|
|
33
|
+
* @throws Error if format is invalid or out of range
|
|
34
|
+
*/
|
|
35
|
+
export declare function parseDuration(s: string): number;
|
|
36
|
+
/**
|
|
37
|
+
* Compute token expiration timestamp from environment variables.
|
|
38
|
+
* Used by both auth.ts (Node.js) and middleware.ts (Edge Runtime).
|
|
39
|
+
*
|
|
40
|
+
* @returns Expiration timestamp (ms since epoch), or null if auth is not enabled
|
|
41
|
+
*/
|
|
42
|
+
export declare function computeExpireAt(): number | null;
|
|
43
|
+
//# sourceMappingURL=auth-config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth-config.d.ts","sourceRoot":"","sources":["../../src/config/auth-config.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,2CAA2C;AAC3C,eAAO,MAAM,gBAAgB,EAAG,eAAwB,CAAC;AAKzD;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,IAAI,MAAM,CAEzE;AAED;;;GAGG;AACH,eAAO,MAAM,mBAAmB,gFAKtB,CAAC;AAeX,mDAAmD;AACnD,eAAO,MAAM,0BAA0B,QAAmB,CAAC;AAQ3D;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAgC/C;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,IAAI,MAAM,GAAG,IAAI,CAgB/C"}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Authentication Configuration Constants
|
|
4
|
+
* Issue #331: Shared constants for auth.ts and middleware.ts
|
|
5
|
+
*
|
|
6
|
+
* CONSTRAINT: This module must be Edge Runtime compatible.
|
|
7
|
+
* No Node.js-specific imports (crypto, fs, etc.) are allowed.
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.DEFAULT_EXPIRE_DURATION_MS = exports.AUTH_EXCLUDED_PATHS = exports.AUTH_COOKIE_NAME = void 0;
|
|
11
|
+
exports.isValidTokenHash = isValidTokenHash;
|
|
12
|
+
exports.parseDuration = parseDuration;
|
|
13
|
+
exports.computeExpireAt = computeExpireAt;
|
|
14
|
+
/** Cookie name for authentication token */
|
|
15
|
+
exports.AUTH_COOKIE_NAME = 'cm_auth_token';
|
|
16
|
+
/** Valid SHA-256 hex string pattern: exactly 64 hex characters */
|
|
17
|
+
const VALID_TOKEN_HASH_PATTERN = /^[0-9a-f]{64}$/;
|
|
18
|
+
/**
|
|
19
|
+
* Validate that a CM_AUTH_TOKEN_HASH value is a well-formed SHA-256 hex string.
|
|
20
|
+
* Used by both auth.ts and middleware.ts to ensure consistent auth-enabled detection.
|
|
21
|
+
* Returns a type predicate so callers can narrow the type after checking.
|
|
22
|
+
*
|
|
23
|
+
* @param hash - The hash string to validate
|
|
24
|
+
* @returns true if the hash is a valid 64-character hex string
|
|
25
|
+
*/
|
|
26
|
+
function isValidTokenHash(hash) {
|
|
27
|
+
return !!hash && VALID_TOKEN_HASH_PATTERN.test(hash);
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Paths excluded from authentication check.
|
|
31
|
+
* S002: Must use === for matching (no startsWith - bypass attack prevention)
|
|
32
|
+
*/
|
|
33
|
+
exports.AUTH_EXCLUDED_PATHS = [
|
|
34
|
+
'/login',
|
|
35
|
+
'/api/auth/login',
|
|
36
|
+
'/api/auth/logout',
|
|
37
|
+
'/api/auth/status',
|
|
38
|
+
];
|
|
39
|
+
// ============================================================
|
|
40
|
+
// Duration Parsing (Edge Runtime compatible)
|
|
41
|
+
// ============================================================
|
|
42
|
+
/** Milliseconds in one minute */
|
|
43
|
+
const MS_PER_MINUTE = 60 * 1000;
|
|
44
|
+
/** Milliseconds in one hour */
|
|
45
|
+
const MS_PER_HOUR = 60 * MS_PER_MINUTE;
|
|
46
|
+
/** Milliseconds in one day */
|
|
47
|
+
const MS_PER_DAY = 24 * MS_PER_HOUR;
|
|
48
|
+
/** Default token expiration duration (24 hours) */
|
|
49
|
+
exports.DEFAULT_EXPIRE_DURATION_MS = 24 * MS_PER_HOUR;
|
|
50
|
+
/** Minimum duration: 1 hour */
|
|
51
|
+
const MIN_DURATION_MS = MS_PER_HOUR;
|
|
52
|
+
/** Maximum duration: 30 days */
|
|
53
|
+
const MAX_DURATION_MS = 30 * MS_PER_DAY;
|
|
54
|
+
/**
|
|
55
|
+
* Parse a duration string into milliseconds.
|
|
56
|
+
* Supported formats: Nh (hours), Nd (days), Nm (minutes)
|
|
57
|
+
* Minimum: 1h, Maximum: 30d
|
|
58
|
+
*
|
|
59
|
+
* @param s - Duration string (e.g., "24h", "7d", "90m")
|
|
60
|
+
* @returns Duration in milliseconds
|
|
61
|
+
* @throws Error if format is invalid or out of range
|
|
62
|
+
*/
|
|
63
|
+
function parseDuration(s) {
|
|
64
|
+
const match = s.match(/^(\d+)([hdm])$/);
|
|
65
|
+
if (!match) {
|
|
66
|
+
throw new Error(`Invalid duration format: "${s}". Use Nh, Nd, or Nm (e.g., "24h", "7d", "90m")`);
|
|
67
|
+
}
|
|
68
|
+
const value = parseInt(match[1], 10);
|
|
69
|
+
const unit = match[2];
|
|
70
|
+
/** Map of duration unit characters to their millisecond multipliers */
|
|
71
|
+
const unitMultipliers = {
|
|
72
|
+
h: MS_PER_HOUR,
|
|
73
|
+
d: MS_PER_DAY,
|
|
74
|
+
m: MS_PER_MINUTE,
|
|
75
|
+
};
|
|
76
|
+
const multiplier = unitMultipliers[unit];
|
|
77
|
+
if (multiplier === undefined) {
|
|
78
|
+
throw new Error(`Invalid duration unit: "${unit}"`);
|
|
79
|
+
}
|
|
80
|
+
const ms = value * multiplier;
|
|
81
|
+
if (ms < MIN_DURATION_MS) {
|
|
82
|
+
throw new Error(`Duration too short: minimum is 1h (60m). Got: "${s}"`);
|
|
83
|
+
}
|
|
84
|
+
if (ms > MAX_DURATION_MS) {
|
|
85
|
+
throw new Error(`Duration too long: maximum is 30d (720h). Got: "${s}"`);
|
|
86
|
+
}
|
|
87
|
+
return ms;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Compute token expiration timestamp from environment variables.
|
|
91
|
+
* Used by both auth.ts (Node.js) and middleware.ts (Edge Runtime).
|
|
92
|
+
*
|
|
93
|
+
* @returns Expiration timestamp (ms since epoch), or null if auth is not enabled
|
|
94
|
+
*/
|
|
95
|
+
function computeExpireAt() {
|
|
96
|
+
const expireStr = process.env.CM_AUTH_EXPIRE;
|
|
97
|
+
const now = Date.now();
|
|
98
|
+
if (expireStr) {
|
|
99
|
+
try {
|
|
100
|
+
return now + parseDuration(expireStr);
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
// Invalid duration format - use default
|
|
104
|
+
return now + exports.DEFAULT_EXPIRE_DURATION_MS;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
// Default 24h if auth is enabled
|
|
108
|
+
if (process.env.CM_AUTH_TOKEN_HASH) {
|
|
109
|
+
return now + exports.DEFAULT_EXPIRE_DURATION_MS;
|
|
110
|
+
}
|
|
111
|
+
return null;
|
|
112
|
+
}
|