@coffeerunhobby/mcp-ha-connect 1.2.0 → 1.3.1
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 +52 -16
- package/CLAUDE.md +75 -0
- package/Dockerfile +5 -0
- package/dist/config.d.ts +10 -0
- package/dist/config.js +59 -3
- package/dist/config.js.map +1 -1
- package/dist/haClient/automations.js +2 -2
- package/dist/haClient/automations.js.map +1 -1
- package/dist/haClient/calendars.js +1 -1
- package/dist/haClient/calendars.js.map +1 -1
- package/dist/haClient/request.d.ts +2 -1
- package/dist/haClient/request.js +20 -11
- package/dist/haClient/request.js.map +1 -1
- package/dist/haClient/services.js +1 -1
- package/dist/haClient/services.js.map +1 -1
- package/dist/haClient/states.js +1 -1
- package/dist/haClient/states.js.map +1 -1
- package/dist/omadaClient/auth.d.ts +2 -1
- package/dist/omadaClient/auth.js +19 -30
- package/dist/omadaClient/auth.js.map +1 -1
- package/dist/omadaClient/request.d.ts +2 -1
- package/dist/omadaClient/request.js +18 -27
- package/dist/omadaClient/request.js.map +1 -1
- package/dist/permissions/index.js +20 -5
- package/dist/permissions/index.js.map +1 -1
- package/dist/server/auth.d.ts +6 -0
- package/dist/server/auth.js +22 -10
- package/dist/server/auth.js.map +1 -1
- package/dist/server/eventSubscription.d.ts +8 -0
- package/dist/server/eventSubscription.js +27 -5
- package/dist/server/eventSubscription.js.map +1 -1
- package/dist/server/http.d.ts +46 -0
- package/dist/server/http.js +143 -28
- package/dist/server/http.js.map +1 -1
- package/dist/server/rateLimiter.d.ts +16 -1
- package/dist/server/rateLimiter.js +32 -11
- package/dist/server/rateLimiter.js.map +1 -1
- package/dist/server/stdio.js +5 -0
- package/dist/server/stdio.js.map +1 -1
- package/dist/server/stream.d.ts +25 -0
- package/dist/server/stream.js +53 -2
- package/dist/server/stream.js.map +1 -1
- package/dist/tools/common.d.ts +11 -10
- package/dist/tools/common.js +20 -2
- package/dist/tools/common.js.map +1 -1
- package/dist/utils/config-validations.d.ts +7 -0
- package/dist/utils/config-validations.js +17 -0
- package/dist/utils/config-validations.js.map +1 -1
- package/dist/utils/jwt.d.ts +15 -1
- package/dist/utils/jwt.js +29 -4
- package/dist/utils/jwt.js.map +1 -1
- package/dist/utils/sanitizeError.d.ts +1 -0
- package/dist/utils/sanitizeError.js +18 -0
- package/dist/utils/sanitizeError.js.map +1 -0
- package/dist/utils/tlsDispatcher.d.ts +34 -0
- package/dist/utils/tlsDispatcher.js +33 -0
- package/dist/utils/tlsDispatcher.js.map +1 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/docker-compose.yml +4 -1
- package/docs/CHANGELOG.md +66 -0
- package/openapi.json +1 -1
- package/package.json +4 -3
- package/src/config.ts +83 -3
- package/src/haClient/automations.ts +2 -2
- package/src/haClient/calendars.ts +1 -1
- package/src/haClient/request.ts +23 -11
- package/src/haClient/services.ts +1 -1
- package/src/haClient/states.ts +1 -1
- package/src/omadaClient/auth.ts +20 -28
- package/src/omadaClient/request.ts +19 -25
- package/src/permissions/index.ts +22 -5
- package/src/server/auth.ts +41 -7
- package/src/server/eventSubscription.ts +30 -5
- package/src/server/http.ts +165 -29
- package/src/server/rateLimiter.ts +43 -11
- package/src/server/stdio.ts +6 -0
- package/src/server/stream.ts +65 -2
- package/src/tools/common.ts +23 -2
- package/src/utils/config-validations.ts +15 -0
- package/src/utils/jwt.ts +47 -4
- package/src/utils/sanitizeError.ts +18 -0
- package/src/utils/tlsDispatcher.ts +40 -0
- package/src/version.ts +1 -1
- package/tests/bdd/configuration.spec.ts +3 -0
- package/tests/config.test.ts +3 -0
- package/tests/permissions/index.test.ts +21 -0
- package/tests/resources/index.test.ts +3 -3
- package/tests/security/accessControl.test.ts +223 -0
- package/tests/security/authentication.test.ts +117 -0
- package/tests/security/configuration.test.ts +170 -0
- package/tests/security/cryptoTransport.test.ts +136 -0
- package/tests/security/dependencies.test.ts +152 -0
- package/tests/security/injection.test.ts +117 -0
- package/tests/security/insecureDesign.test.ts +122 -0
- package/tests/security/logging.test.ts +131 -0
- package/tests/security/resourceConsumption.test.ts +133 -0
- package/tests/tools/ai/analyzeSensors.test.ts +3 -3
- package/tests/tools/common.test.ts +5 -4
- package/tests/tools/homeassistant/handlers.test.ts +2 -2
- package/tests/tools/omada/handlers.test.ts +3 -3
package/dist/omadaClient/auth.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { logger } from '../utils/logger.js';
|
|
2
|
+
import { createTlsDispatcher } from '../utils/tlsDispatcher.js';
|
|
2
3
|
const TOKEN_EXPIRY_BUFFER_SECONDS = 30;
|
|
3
4
|
/**
|
|
4
5
|
* Authentication state management for the Omada client.
|
|
@@ -12,14 +13,15 @@ export class AuthManager {
|
|
|
12
13
|
clientSecret;
|
|
13
14
|
omadacId;
|
|
14
15
|
timeout;
|
|
15
|
-
strictSsl
|
|
16
|
+
/** Per-client undici dispatcher; set only when strictSsl is disabled (H2). */
|
|
17
|
+
dispatcher;
|
|
16
18
|
constructor(config) {
|
|
17
19
|
this.baseUrl = config.baseUrl;
|
|
18
20
|
this.clientId = config.clientId;
|
|
19
21
|
this.clientSecret = config.clientSecret;
|
|
20
22
|
this.omadacId = config.omadacId;
|
|
21
23
|
this.timeout = config.timeout ?? 30000;
|
|
22
|
-
this.
|
|
24
|
+
this.dispatcher = createTlsDispatcher(config.strictSsl);
|
|
23
25
|
}
|
|
24
26
|
/**
|
|
25
27
|
* Get the current access token, refreshing if necessary.
|
|
@@ -75,35 +77,22 @@ export class AuthManager {
|
|
|
75
77
|
body.omadacId = this.omadacId;
|
|
76
78
|
}
|
|
77
79
|
try {
|
|
78
|
-
// For self-signed certificates
|
|
79
|
-
//
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
body: JSON.stringify(body),
|
|
93
|
-
signal: AbortSignal.timeout(this.timeout),
|
|
94
|
-
});
|
|
95
|
-
}
|
|
96
|
-
finally {
|
|
97
|
-
// Restore original setting
|
|
98
|
-
if (!this.strictSsl) {
|
|
99
|
-
if (originalRejectUnauthorized === undefined) {
|
|
100
|
-
delete process.env.NODE_TLS_REJECT_UNAUTHORIZED;
|
|
101
|
-
}
|
|
102
|
-
else {
|
|
103
|
-
process.env.NODE_TLS_REJECT_UNAUTHORIZED = originalRejectUnauthorized;
|
|
104
|
-
}
|
|
105
|
-
}
|
|
80
|
+
// For self-signed certificates we relax TLS validation for THIS client
|
|
81
|
+
// only, via a per-instance undici dispatcher (H2) — never by toggling the
|
|
82
|
+
// process-global NODE_TLS_REJECT_UNAUTHORIZED env var.
|
|
83
|
+
const init = {
|
|
84
|
+
method: 'POST',
|
|
85
|
+
headers: {
|
|
86
|
+
'Content-Type': 'application/json',
|
|
87
|
+
Accept: 'application/json',
|
|
88
|
+
},
|
|
89
|
+
body: JSON.stringify(body),
|
|
90
|
+
signal: AbortSignal.timeout(this.timeout),
|
|
91
|
+
};
|
|
92
|
+
if (this.dispatcher) {
|
|
93
|
+
init.dispatcher = this.dispatcher;
|
|
106
94
|
}
|
|
95
|
+
const response = await fetch(url.toString(), init);
|
|
107
96
|
if (!response.ok) {
|
|
108
97
|
const errorText = await response.text();
|
|
109
98
|
logger.error('Omada authentication HTTP error', {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/omadaClient/auth.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/omadaClient/auth.ts"],"names":[],"mappings":"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"}
|
|
@@ -19,7 +19,8 @@ export declare class RequestHandler {
|
|
|
19
19
|
private readonly auth;
|
|
20
20
|
private readonly baseUrl;
|
|
21
21
|
private readonly timeout;
|
|
22
|
-
|
|
22
|
+
/** Per-client undici dispatcher; set only when strictSsl is disabled (H2). */
|
|
23
|
+
private readonly dispatcher?;
|
|
23
24
|
constructor(config: RequestHandlerConfig, auth: AuthManager);
|
|
24
25
|
/**
|
|
25
26
|
* Make a GET request to the Omada API.
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { logger } from '../utils/logger.js';
|
|
2
|
+
import { createTlsDispatcher } from '../utils/tlsDispatcher.js';
|
|
2
3
|
const DEFAULT_PAGE_SIZE = 200;
|
|
3
4
|
/**
|
|
4
5
|
* HTTP request handler for Omada API calls with authentication and retry logic.
|
|
@@ -7,12 +8,13 @@ export class RequestHandler {
|
|
|
7
8
|
auth;
|
|
8
9
|
baseUrl;
|
|
9
10
|
timeout;
|
|
10
|
-
strictSsl
|
|
11
|
+
/** Per-client undici dispatcher; set only when strictSsl is disabled (H2). */
|
|
12
|
+
dispatcher;
|
|
11
13
|
constructor(config, auth) {
|
|
12
14
|
this.auth = auth;
|
|
13
15
|
this.baseUrl = config.baseUrl;
|
|
14
16
|
this.timeout = config.timeout ?? 30000;
|
|
15
|
-
this.
|
|
17
|
+
this.dispatcher = createTlsDispatcher(config.strictSsl);
|
|
16
18
|
}
|
|
17
19
|
/**
|
|
18
20
|
* Make a GET request to the Omada API.
|
|
@@ -67,32 +69,21 @@ export class RequestHandler {
|
|
|
67
69
|
data: this.sanitizePayload(config.data),
|
|
68
70
|
});
|
|
69
71
|
try {
|
|
70
|
-
// For self-signed certificates
|
|
71
|
-
//
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
});
|
|
84
|
-
}
|
|
85
|
-
finally {
|
|
86
|
-
// Restore original setting
|
|
87
|
-
if (!this.strictSsl) {
|
|
88
|
-
if (originalRejectUnauthorized === undefined) {
|
|
89
|
-
delete process.env.NODE_TLS_REJECT_UNAUTHORIZED;
|
|
90
|
-
}
|
|
91
|
-
else {
|
|
92
|
-
process.env.NODE_TLS_REJECT_UNAUTHORIZED = originalRejectUnauthorized;
|
|
93
|
-
}
|
|
94
|
-
}
|
|
72
|
+
// For self-signed certificates we relax TLS validation for THIS client
|
|
73
|
+
// only, via a per-instance undici dispatcher (H2). Node's global fetch
|
|
74
|
+
// ignores the legacy `agent` option, and toggling the process-global
|
|
75
|
+
// NODE_TLS_REJECT_UNAUTHORIZED env var would disable validation for every
|
|
76
|
+
// concurrent outbound request in the process.
|
|
77
|
+
const init = {
|
|
78
|
+
method,
|
|
79
|
+
headers,
|
|
80
|
+
body: config.data ? JSON.stringify(config.data) : undefined,
|
|
81
|
+
signal: AbortSignal.timeout(this.timeout),
|
|
82
|
+
};
|
|
83
|
+
if (this.dispatcher) {
|
|
84
|
+
init.dispatcher = this.dispatcher;
|
|
95
85
|
}
|
|
86
|
+
const response = await fetch(url.toString(), init);
|
|
96
87
|
logger.info('Omada response', {
|
|
97
88
|
method,
|
|
98
89
|
url: config.url,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"request.js","sourceRoot":"","sources":["../../src/omadaClient/request.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"request.js","sourceRoot":"","sources":["../../src/omadaClient/request.ts"],"names":[],"mappings":"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"}
|
|
@@ -19,24 +19,39 @@ export const Role = {
|
|
|
19
19
|
ADMIN: Permission.QUERY | Permission.CONTROL | Permission.NOTIFY | Permission.CONFIGURE | Permission.AI | Permission.ADMIN,
|
|
20
20
|
SUPERUSER: 0xFF, // All permissions
|
|
21
21
|
};
|
|
22
|
+
/**
|
|
23
|
+
* Resolve a role name to its permission mask, case-insensitively.
|
|
24
|
+
*
|
|
25
|
+
* Config (and JSON) commonly use lowercase role names ("operator") while the
|
|
26
|
+
* `Role` map is keyed uppercase ("OPERATOR"). Normalize here so BOTH the per-user
|
|
27
|
+
* role and the `defaultRole` fall-back resolve consistently. An unknown/undefined
|
|
28
|
+
* name fails closed to `Role.NONE` (never `undefined`, which downstream coerces to
|
|
29
|
+
* a zero mask anyway but is easy to mishandle).
|
|
30
|
+
*/
|
|
31
|
+
function resolveRoleName(name) {
|
|
32
|
+
if (!name) {
|
|
33
|
+
return Role.NONE;
|
|
34
|
+
}
|
|
35
|
+
const key = name.toUpperCase();
|
|
36
|
+
return Role[key] ?? Role.NONE;
|
|
37
|
+
}
|
|
22
38
|
/**
|
|
23
39
|
* Get permission mask for a user by their JWT sub claim
|
|
24
40
|
*/
|
|
25
41
|
export function getUserPermissions(sub, config) {
|
|
42
|
+
const fallback = resolveRoleName(config.defaultRole);
|
|
26
43
|
if (!sub) {
|
|
27
|
-
return
|
|
44
|
+
return fallback;
|
|
28
45
|
}
|
|
29
46
|
const user = config.users.find((u) => u.sub === sub);
|
|
30
47
|
if (!user) {
|
|
31
|
-
return
|
|
48
|
+
return fallback;
|
|
32
49
|
}
|
|
33
50
|
// Direct mask takes precedence over role
|
|
34
51
|
if (user.mask !== undefined) {
|
|
35
52
|
return user.mask;
|
|
36
53
|
}
|
|
37
|
-
|
|
38
|
-
const roleKey = user.role?.toUpperCase();
|
|
39
|
-
return roleKey && Role[roleKey] !== undefined ? Role[roleKey] : Role.NONE;
|
|
54
|
+
return resolveRoleName(user.role);
|
|
40
55
|
}
|
|
41
56
|
/**
|
|
42
57
|
* Check if a permission mask includes required permission
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/permissions/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,mFAAmF;AACnF,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,KAAK,EAAE,CAAC,EAAO,6DAA6D;IAC5E,SAAS,EAAE,CAAC,EAAG,qCAAqC;IACpD,OAAO,EAAE,CAAC,EAAK,0CAA0C;IACzD,KAAK,EAAE,CAAC,EAAO,qCAAqC;IACpD,MAAM,EAAE,EAAE,EAAK,qBAAqB;IACpC,EAAE,EAAE,EAAE,EAAS,2BAA2B;CAClC,CAAC;AAIX,iDAAiD;AACjD,MAAM,CAAC,MAAM,IAAI,GAAG;IAClB,IAAI,EAAE,CAAC;IACP,QAAQ,EAAE,UAAU,CAAC,KAAK;IAC1B,QAAQ,EAAE,UAAU,CAAC,KAAK,GAAG,UAAU,CAAC,OAAO,GAAG,UAAU,CAAC,MAAM;IACnE,WAAW,EAAE,UAAU,CAAC,KAAK,GAAG,UAAU,CAAC,OAAO,GAAG,UAAU,CAAC,MAAM,GAAG,UAAU,CAAC,SAAS;IAC7F,KAAK,EAAE,UAAU,CAAC,KAAK,GAAG,UAAU,CAAC,OAAO,GAAG,UAAU,CAAC,MAAM,GAAG,UAAU,CAAC,SAAS,GAAG,UAAU,CAAC,EAAE,GAAG,UAAU,CAAC,KAAK;IAC1H,SAAS,EAAE,IAAI,EAAE,kBAAkB;CAC3B,CAAC;AAgBX
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/permissions/index.ts"],"names":[],"mappings":"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"}
|
package/dist/server/auth.d.ts
CHANGED
|
@@ -11,6 +11,12 @@ export interface AuthConfig {
|
|
|
11
11
|
secret?: string;
|
|
12
12
|
permissions?: PermissionsConfig;
|
|
13
13
|
skipPaths?: string[];
|
|
14
|
+
/** Reject tokens without an exp claim (H3). */
|
|
15
|
+
requireExp?: boolean;
|
|
16
|
+
/** Expected JWT issuer, if any (L6). */
|
|
17
|
+
issuer?: string;
|
|
18
|
+
/** Expected JWT audience, if any (L6). */
|
|
19
|
+
audience?: string;
|
|
14
20
|
}
|
|
15
21
|
export interface AuthResult {
|
|
16
22
|
authenticated: boolean;
|
package/dist/server/auth.js
CHANGED
|
@@ -2,12 +2,11 @@
|
|
|
2
2
|
* Bearer token authentication middleware for HTTP server
|
|
3
3
|
*/
|
|
4
4
|
import { verifyJwt } from '../utils/jwt.js';
|
|
5
|
-
import { getUserPermissions } from '../permissions/index.js';
|
|
5
|
+
import { getUserPermissions, Role } from '../permissions/index.js';
|
|
6
6
|
import { logger } from '../utils/logger.js';
|
|
7
|
-
/**
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
function validateJwt(req, secret, permConfig) {
|
|
7
|
+
/** Emit the "no exp" advisory at most once per process. */
|
|
8
|
+
let warnedNoExp = false;
|
|
9
|
+
function validateJwt(req, secret, permConfig, policy = {}) {
|
|
11
10
|
const authHeader = req.headers.authorization;
|
|
12
11
|
if (!authHeader) {
|
|
13
12
|
return { authenticated: false, error: 'Missing Authorization header' };
|
|
@@ -16,14 +15,27 @@ function validateJwt(req, secret, permConfig) {
|
|
|
16
15
|
return { authenticated: false, error: 'Expected: Bearer <token>' };
|
|
17
16
|
}
|
|
18
17
|
const token = authHeader.slice(7);
|
|
19
|
-
const result = verifyJwt(token, secret
|
|
18
|
+
const result = verifyJwt(token, secret, {
|
|
19
|
+
requireExp: policy.requireExp,
|
|
20
|
+
issuer: policy.issuer,
|
|
21
|
+
audience: policy.audience,
|
|
22
|
+
clockSkewSec: 60,
|
|
23
|
+
});
|
|
20
24
|
if (!result.valid) {
|
|
21
25
|
return { authenticated: false, error: result.error };
|
|
22
26
|
}
|
|
23
|
-
//
|
|
27
|
+
// H3: warn (once) when accepting a token that can never expire.
|
|
28
|
+
if (!policy.requireExp && result.payload?.exp === undefined && !warnedNoExp) {
|
|
29
|
+
warnedNoExp = true;
|
|
30
|
+
logger.warn('Accepting a JWT without an exp claim; set MCP_AUTH_REQUIRE_EXP=true to enforce token expiry');
|
|
31
|
+
}
|
|
32
|
+
// Get user permissions from config.
|
|
33
|
+
// L3: fail closed when no permission map is configured — an authenticated token
|
|
34
|
+
// with no RBAC mapping gets NONE, never full access. (In the real server path
|
|
35
|
+
// `permConfig` is always supplied by loadConfig, so this is defense in depth.)
|
|
24
36
|
const permissions = permConfig
|
|
25
37
|
? getUserPermissions(result.payload?.sub, permConfig)
|
|
26
|
-
:
|
|
38
|
+
: Role.NONE;
|
|
27
39
|
return { authenticated: true, payload: result.payload, permissions };
|
|
28
40
|
}
|
|
29
41
|
/**
|
|
@@ -31,7 +43,7 @@ function validateJwt(req, secret, permConfig) {
|
|
|
31
43
|
* Returns true if request should proceed, false if blocked
|
|
32
44
|
*/
|
|
33
45
|
export function createAuthMiddleware(config) {
|
|
34
|
-
const { method, secret, permissions: permConfig, skipPaths = [] } = config;
|
|
46
|
+
const { method, secret, permissions: permConfig, skipPaths = [], requireExp, issuer, audience } = config;
|
|
35
47
|
return (req, res) => {
|
|
36
48
|
if (method === 'none') {
|
|
37
49
|
// No auth = full permissions via AuthInfo.extra
|
|
@@ -58,7 +70,7 @@ export function createAuthMiddleware(config) {
|
|
|
58
70
|
res.end(JSON.stringify({ error: 'Server misconfiguration' }));
|
|
59
71
|
return false;
|
|
60
72
|
}
|
|
61
|
-
const result = validateJwt(req, secret, permConfig);
|
|
73
|
+
const result = validateJwt(req, secret, permConfig, { requireExp, issuer, audience });
|
|
62
74
|
if (!result.authenticated) {
|
|
63
75
|
logger.warn('Auth failed', { path, error: result.error, ip: req.socket.remoteAddress });
|
|
64
76
|
res.statusCode = 401;
|
package/dist/server/auth.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/server/auth.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,EAAE,SAAS,EAAmB,MAAM,iBAAiB,CAAC;AAC7D,OAAO,EAAE,kBAAkB,EAA0B,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/server/auth.ts"],"names":[],"mappings":"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"}
|
|
@@ -15,6 +15,14 @@ export interface SSEClient {
|
|
|
15
15
|
lastEventAt?: Date;
|
|
16
16
|
eventCount: number;
|
|
17
17
|
}
|
|
18
|
+
/**
|
|
19
|
+
* Generate an unguessable SSE client ID (L5).
|
|
20
|
+
*
|
|
21
|
+
* Uses a CSPRNG (randomUUID) rather than Math.random()/Date.now(), so a client
|
|
22
|
+
* ID cannot be predicted or enumerated by an observer who knows roughly when a
|
|
23
|
+
* connection was opened.
|
|
24
|
+
*/
|
|
25
|
+
export declare function generateClientId(): string;
|
|
18
26
|
/**
|
|
19
27
|
* Handle SSE subscription request
|
|
20
28
|
*/
|
|
@@ -2,9 +2,22 @@
|
|
|
2
2
|
* SSE Event Subscription Endpoint
|
|
3
3
|
* Allows HTTP clients to subscribe to Home Assistant events via Server-Sent Events
|
|
4
4
|
*/
|
|
5
|
+
import { randomUUID } from 'node:crypto';
|
|
5
6
|
import { logger } from '../utils/logger.js';
|
|
7
|
+
import { sanitizeError } from '../utils/sanitizeError.js';
|
|
8
|
+
import { Permission, hasPermission } from '../permissions/index.js';
|
|
6
9
|
// Store active SSE clients
|
|
7
10
|
const clients = new Map();
|
|
11
|
+
/**
|
|
12
|
+
* Generate an unguessable SSE client ID (L5).
|
|
13
|
+
*
|
|
14
|
+
* Uses a CSPRNG (randomUUID) rather than Math.random()/Date.now(), so a client
|
|
15
|
+
* ID cannot be predicted or enumerated by an observer who knows roughly when a
|
|
16
|
+
* connection was opened.
|
|
17
|
+
*/
|
|
18
|
+
export function generateClientId() {
|
|
19
|
+
return randomUUID();
|
|
20
|
+
}
|
|
8
21
|
/**
|
|
9
22
|
* Parse query parameters from URL
|
|
10
23
|
*/
|
|
@@ -39,20 +52,27 @@ function sendSSEComment(res, comment) {
|
|
|
39
52
|
* Handle SSE subscription request
|
|
40
53
|
*/
|
|
41
54
|
export async function handleEventSubscription(req, res, eventSubscriber) {
|
|
42
|
-
|
|
55
|
+
// L2: the SSE stream reads entity state — require QUERY, like every other read.
|
|
56
|
+
// Auth middleware has already populated req.auth; fail closed if the mask is absent.
|
|
57
|
+
const userPermissions = req.auth?.extra?.permissions ?? 0;
|
|
58
|
+
if (!hasPermission(userPermissions, Permission.QUERY)) {
|
|
59
|
+
logger.warn('SSE subscription denied: missing QUERY permission');
|
|
60
|
+
res.writeHead(403, { 'Content-Type': 'application/json' });
|
|
61
|
+
res.end(JSON.stringify({ error: 'Forbidden', message: 'SSE event subscription requires QUERY permission' }));
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
const clientId = generateClientId();
|
|
43
65
|
const params = parseQueryParams(req.url ?? '');
|
|
44
66
|
// Parse filter parameters
|
|
45
67
|
const domain = params.get('domain') ?? undefined;
|
|
46
68
|
const entityId = params.get('entity_id') ?? undefined;
|
|
47
69
|
const eventTypesParam = params.get('event_types');
|
|
48
70
|
const eventTypes = eventTypesParam ? eventTypesParam.split(',').map(t => t.trim()) : undefined;
|
|
49
|
-
const token = params.get('token');
|
|
50
71
|
logger.info('New SSE subscription request', {
|
|
51
72
|
clientId,
|
|
52
73
|
domain,
|
|
53
74
|
entityId,
|
|
54
75
|
eventTypes,
|
|
55
|
-
hasToken: !!token,
|
|
56
76
|
});
|
|
57
77
|
// Set SSE headers
|
|
58
78
|
res.writeHead(200, {
|
|
@@ -89,9 +109,10 @@ export async function handleEventSubscription(req, res, eventSubscriber) {
|
|
|
89
109
|
}
|
|
90
110
|
catch (error) {
|
|
91
111
|
logger.error('Failed to connect event subscriber', { error });
|
|
112
|
+
// M6: log detail above; the SSE client gets a generic message only.
|
|
92
113
|
sendSSEEvent(res, 'error', {
|
|
93
114
|
error: 'Failed to connect to Home Assistant',
|
|
94
|
-
message:
|
|
115
|
+
message: sanitizeError(error),
|
|
95
116
|
});
|
|
96
117
|
res.end();
|
|
97
118
|
clients.delete(clientId);
|
|
@@ -156,9 +177,10 @@ export async function handleEventSubscription(req, res, eventSubscriber) {
|
|
|
156
177
|
}
|
|
157
178
|
catch (error) {
|
|
158
179
|
logger.error('Failed to subscribe to events', { error, clientId });
|
|
180
|
+
// M6: log detail above; the SSE client gets a generic message only.
|
|
159
181
|
sendSSEEvent(res, 'error', {
|
|
160
182
|
error: 'Failed to subscribe to events',
|
|
161
|
-
message:
|
|
183
|
+
message: sanitizeError(error),
|
|
162
184
|
});
|
|
163
185
|
}
|
|
164
186
|
// Keep-alive ping every 30 seconds
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"eventSubscription.js","sourceRoot":"","sources":["../../src/server/eventSubscription.ts"],"names":[],"mappings":"AAAA;;;GAGG;
|
|
1
|
+
{"version":3,"file":"eventSubscription.js","sourceRoot":"","sources":["../../src/server/eventSubscription.ts"],"names":[],"mappings":"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"}
|
package/dist/server/http.d.ts
CHANGED
|
@@ -2,10 +2,56 @@
|
|
|
2
2
|
* HTTP server for MCP with Streamable HTTP transport
|
|
3
3
|
* Includes health check, CORS support, rate limiting, and real-time event subscriptions
|
|
4
4
|
*/
|
|
5
|
+
import { type IncomingMessage, type ServerResponse } from 'node:http';
|
|
5
6
|
import type { EnvironmentConfig } from '../config.js';
|
|
6
7
|
import type { HaClient } from '../haClient/index.js';
|
|
7
8
|
import type { LocalAIClient } from '../localAI/index.js';
|
|
8
9
|
import type { OmadaClient } from '../omadaClient/index.js';
|
|
10
|
+
/**
|
|
11
|
+
* Maximum accepted request body size, in bytes (M5 / OWASP API4:2023).
|
|
12
|
+
* 1 MB is comfortably above any legitimate MCP/JSON-RPC or REST payload.
|
|
13
|
+
*/
|
|
14
|
+
export declare const MAX_BODY_BYTES: number;
|
|
15
|
+
/**
|
|
16
|
+
* Parse a JSON request body, enforcing a hard size cap (M5).
|
|
17
|
+
*
|
|
18
|
+
* If the streamed body exceeds `maxBytes` the promise rejects with a 413-tagged
|
|
19
|
+
* error and the socket is destroyed, so an attacker cannot exhaust memory by
|
|
20
|
+
* streaming an unbounded payload. Buffering stops the moment the cap is crossed.
|
|
21
|
+
*/
|
|
22
|
+
export declare function parseBody(req: IncomingMessage, maxBytes?: number): Promise<unknown>;
|
|
23
|
+
/**
|
|
24
|
+
* Apply baseline security response headers (L4 / OWASP A05:2021).
|
|
25
|
+
*
|
|
26
|
+
* Cheap, static defenses applied to every response: block MIME sniffing,
|
|
27
|
+
* forbid framing (clickjacking), and avoid leaking full URLs in the Referer.
|
|
28
|
+
*/
|
|
29
|
+
export declare function applySecurityHeaders(res: ServerResponse): void;
|
|
30
|
+
/**
|
|
31
|
+
* Handle health check endpoint (M7 / OWASP A09:2021).
|
|
32
|
+
*
|
|
33
|
+
* `/health` is unauthenticated (it is on the auth skip-list so container/orchestrator
|
|
34
|
+
* probes can reach it). It therefore returns liveness ONLY — never version, auth
|
|
35
|
+
* method, AI provider URLs, client counts, or any other internal configuration that
|
|
36
|
+
* would aid an unauthenticated attacker fingerprinting the deployment. Operators who
|
|
37
|
+
* need that detail can query the authenticated MCP tools (e.g. `getVersion`).
|
|
38
|
+
*/
|
|
39
|
+
export declare function handleHealthCheck(res: ServerResponse): void;
|
|
40
|
+
/**
|
|
41
|
+
* Resolve the permission a REST `/api/*` route requires.
|
|
42
|
+
*
|
|
43
|
+
* Each route mirrors the `Permission.*` bit of its MCP-tool twin so the REST
|
|
44
|
+
* bridge cannot be used to bypass RBAC (finding H1 / OWASP A01:2021, BFLA).
|
|
45
|
+
* Read routes require QUERY; the service-call route requires CONTROL (matching
|
|
46
|
+
* the `callService` tool). Unknown routes return `null` — the handler falls
|
|
47
|
+
* through to a 404, which needs no permission.
|
|
48
|
+
*/
|
|
49
|
+
export declare function requiredRestPermission(method: string, pathname: string): number | null;
|
|
50
|
+
/**
|
|
51
|
+
* Handle REST API requests for Open WebUI compatibility
|
|
52
|
+
* These endpoints translate REST calls to Home Assistant API calls
|
|
53
|
+
*/
|
|
54
|
+
export declare function handleRestApi(req: IncomingMessage, res: ServerResponse, url: string, client: HaClient): Promise<void>;
|
|
9
55
|
/**
|
|
10
56
|
* Start HTTP server
|
|
11
57
|
*/
|