@open-everywhere/provider-teams 0.1.3 → 0.1.5
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/NOTICE +34 -4
- package/agent/channels/teams-graph-boundary.ts +13 -54
- package/agent/channels/teams-graph-roles.ts +62 -0
- package/agent/ops.ts +7 -3
- package/dist/agent/channels/teams-graph-boundary.d.ts +2 -22
- package/dist/agent/channels/teams-graph-boundary.d.ts.map +1 -1
- package/dist/agent/channels/teams-graph-boundary.js +7 -50
- package/dist/agent/channels/teams-graph-boundary.js.map +1 -1
- package/dist/agent/channels/teams-graph-roles.d.ts +25 -0
- package/dist/agent/channels/teams-graph-roles.d.ts.map +1 -0
- package/dist/agent/channels/teams-graph-roles.js +58 -0
- package/dist/agent/channels/teams-graph-roles.js.map +1 -0
- package/dist/agent/ops.d.ts +2 -2
- package/dist/agent/ops.d.ts.map +1 -1
- package/dist/agent/ops.js +7 -3
- package/dist/agent/ops.js.map +1 -1
- package/package.json +7 -3
package/NOTICE
CHANGED
|
@@ -8,10 +8,40 @@ This product is licensed under the Apache License, Version 2.0 (see LICENSE).
|
|
|
8
8
|
Built on Vercel Eve (https://github.com/vercel/eve), licensed under the
|
|
9
9
|
Apache License, Version 2.0.
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
Modifications to eve
|
|
14
|
+
|
|
15
|
+
The @open-everywhere/core package distributes patches/eve+0.54.3.patch. That
|
|
16
|
+
patch file is a modification of eve 0.54.3. Vercel licenses eve 0.54.3 under the
|
|
17
|
+
Apache License, Version 2.0. The copyright notice of eve 0.54.3 reads
|
|
18
|
+
"Copyright 2026 Vercel, Inc. and contributors". The Open Everywhere authors made
|
|
19
|
+
the changes in the patch file.
|
|
20
|
+
|
|
21
|
+
The patch modifies six files of eve 0.54.3:
|
|
22
|
+
|
|
23
|
+
dist/src/context/dynamic-tool-lifecycle.js
|
|
24
|
+
dist/src/execution/web-fetch/request.js
|
|
25
|
+
dist/src/harness/tool-loop.js
|
|
26
|
+
dist/src/internal/authored-package-boundary.js
|
|
27
|
+
dist/src/protocol/message.d.ts
|
|
28
|
+
dist/src/tasks/session-index.js
|
|
29
|
+
|
|
30
|
+
Open Everywhere does not ship the eve package. eve is a peer dependency, so the
|
|
31
|
+
consumer installs eve 0.54.3 from the registry. The patch file carries the
|
|
32
|
+
modified portions of eve. The oe-eve-patch command, which
|
|
33
|
+
@open-everywhere/core ships, applies them to the eve that the consumer
|
|
34
|
+
installed. The command writes no file outside the consumer's own project.
|
|
35
|
+
|
|
36
|
+
The attribution notice of eve 0.54.3 follows, as section 4(d) of the Apache
|
|
37
|
+
License, Version 2.0 requires. The notices for eve's bundled code-mode runtime
|
|
38
|
+
are not reproduced, because the patch modifies no part of that runtime.
|
|
39
|
+
|
|
40
|
+
eve
|
|
41
|
+
Copyright 2026 Vercel, Inc. and contributors
|
|
42
|
+
|
|
43
|
+
This product includes software developed at Vercel, Inc.
|
|
44
|
+
(https://vercel.com/).
|
|
15
45
|
|
|
16
46
|
---
|
|
17
47
|
|
|
@@ -46,7 +46,8 @@
|
|
|
46
46
|
// 🔴 KNOWN LIMIT, STATED RATHER THAN HIDDEN: a denylist reports "per-team" for any FUTURE
|
|
47
47
|
// tenant-wide role Microsoft adds to those endpoints. An allowlist is not available (point 1 —
|
|
48
48
|
// there is nothing positive to match). Re-check the two API pages when Graph changes; the list
|
|
49
|
-
//
|
|
49
|
+
// in teams-graph-roles.ts (TENANT_WIDE_CHANNEL_READ_ROLES) is the whole of the claim, and
|
|
50
|
+
// TEAMS_READ_BOUNDARY_SOURCES below records where it came from.
|
|
50
51
|
//
|
|
51
52
|
// NO SIGNATURE VERIFICATION, ON PURPOSE. We decode a token WE minted, over TLS, from
|
|
52
53
|
// login.microsoftonline.com, seconds earlier. It is not attacker-supplied, and the decoded value
|
|
@@ -55,16 +56,18 @@
|
|
|
55
56
|
// of the token itself.
|
|
56
57
|
|
|
57
58
|
import { appendAudit, deploymentOrg } from "@open-everywhere/core/agent/adapter-api.js";
|
|
59
|
+
import { boundaryFromRoles, decodeJwtRoles, tenantWideRolesIn, type TeamsReadBoundary } from "./teams-graph-roles.js";
|
|
58
60
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
61
|
+
// The pure predicate lives in teams-graph-roles.ts, which imports nothing from core (OPE-2219). The
|
|
62
|
+
// ops arm needs only the predicate, and this file's audit row needs the main core barrel, which
|
|
63
|
+
// costs about 1.9 s to load. Re-exported here so this file's public names stay the same.
|
|
64
|
+
export {
|
|
65
|
+
boundaryFromRoles,
|
|
66
|
+
boundaryOfGraphToken,
|
|
67
|
+
decodeJwtRoles,
|
|
68
|
+
TENANT_WIDE_CHANNEL_READ_ROLES,
|
|
69
|
+
type TeamsReadBoundary,
|
|
70
|
+
} from "./teams-graph-roles.js";
|
|
68
71
|
|
|
69
72
|
/** Where the denylist came from — quoted in the runbook so a future reader can re-verify it. */
|
|
70
73
|
export const TEAMS_READ_BOUNDARY_SOURCES: readonly string[] = [
|
|
@@ -76,50 +79,6 @@ export const TEAMS_READ_BOUNDARY_SOURCES: readonly string[] = [
|
|
|
76
79
|
/** The audit action that records a boundary determination. */
|
|
77
80
|
const TEAMS_READ_BOUNDARY_ACTION = "teams.read_boundary";
|
|
78
81
|
|
|
79
|
-
/**
|
|
80
|
-
* The `roles` claim of a JWT's payload, or `undefined` when the token does not parse as one.
|
|
81
|
-
*
|
|
82
|
-
* Returns an EMPTY array — not `undefined` — for a token that parses but carries no `roles` claim:
|
|
83
|
-
* that is a real, meaningful state (an app registration with no consented Graph application
|
|
84
|
-
* permissions), and it is exactly the per-team case. Only a genuine parse failure is `undefined`,
|
|
85
|
-
* which is what keeps "malformed" from being reported as "per-team". Pure.
|
|
86
|
-
*/
|
|
87
|
-
export function decodeJwtRoles(token: string): readonly string[] | undefined {
|
|
88
|
-
const parts = token.split(".");
|
|
89
|
-
if (parts.length !== 3 || parts[1].length === 0) return undefined;
|
|
90
|
-
let payload: unknown;
|
|
91
|
-
try {
|
|
92
|
-
payload = JSON.parse(Buffer.from(parts[1], "base64url").toString("utf-8"));
|
|
93
|
-
} catch {
|
|
94
|
-
return undefined;
|
|
95
|
-
}
|
|
96
|
-
// An ARRAY passes `typeof === "object"`, so it needs its own rejection: a JWT payload is a JSON
|
|
97
|
-
// object, and letting an array through would read its absent `roles` as [] — reporting "per-team"
|
|
98
|
-
// for a token that never parsed as a claims set at all.
|
|
99
|
-
if (typeof payload !== "object" || payload === null || Array.isArray(payload)) return undefined;
|
|
100
|
-
const roles = (payload as { roles?: unknown }).roles;
|
|
101
|
-
if (roles === undefined) return [];
|
|
102
|
-
if (!Array.isArray(roles)) return undefined;
|
|
103
|
-
return roles.filter((r): r is string => typeof r === "string");
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
/** Every tenant-wide role the token actually carries (empty on the per-team path). Pure. */
|
|
107
|
-
function tenantWideRolesIn(roles: readonly string[]): readonly string[] {
|
|
108
|
-
return TENANT_WIDE_CHANNEL_READ_ROLES.filter((r) => roles.includes(r));
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
/** The boundary a decoded `roles` claim implies — see the header for the one-sided reasoning.
|
|
112
|
-
* `undefined` roles (a token that did not parse) yield `"unknown"`, never a guess. Pure. */
|
|
113
|
-
export function boundaryFromRoles(roles: readonly string[] | undefined): TeamsReadBoundary {
|
|
114
|
-
if (roles === undefined) return "unknown";
|
|
115
|
-
return tenantWideRolesIn(roles).length > 0 ? "tenant-wide" : "per-team";
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
/** The boundary a minted Graph token implies. Pure; never logs or returns any part of the token. */
|
|
119
|
-
export function boundaryOfGraphToken(token: string): TeamsReadBoundary {
|
|
120
|
-
return boundaryFromRoles(decodeJwtRoles(token));
|
|
121
|
-
}
|
|
122
|
-
|
|
123
82
|
// --- the process-held determination + its audit row -------------------------------------------
|
|
124
83
|
|
|
125
84
|
let held: TeamsReadBoundary = "unknown";
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
// THE PURE HALF of the Teams channel-read boundary predicate (OPE-428): decode a Graph token's
|
|
2
|
+
// `roles` claim and map it to a boundary. teams-graph-boundary.ts holds the reasoning (why a
|
|
3
|
+
// denylist, what it can miss) and the process-held determination with its audit row.
|
|
4
|
+
//
|
|
5
|
+
// 🔴 NO CORE IMPORT, ON PURPOSE (OPE-2219). The ops arm (agent/ops.ts) runs this predicate in the
|
|
6
|
+
// standalone ops MCP server. teams-graph-boundary.ts imports the main core barrel for its audit row,
|
|
7
|
+
// and that barrel loads most of core: about 1.9 s on a warm Windows machine. Boundary rule 4d
|
|
8
|
+
// (packages/core/scripts/boundary-test.ts) refuses an ops arm that reaches the main barrel.
|
|
9
|
+
|
|
10
|
+
/** Which boundary governs this deployment's Teams channel reads.
|
|
11
|
+
* - `per-team` — Microsoft enforces the team boundary (the RSC path, shipped default).
|
|
12
|
+
* - `tenant-wide` — Microsoft enforces NO team boundary; only OE's domain allowlist remains.
|
|
13
|
+
* - `unknown` — not determined yet, or the minted token did not parse. */
|
|
14
|
+
export type TeamsReadBoundary = "per-team" | "tenant-wide" | "unknown";
|
|
15
|
+
|
|
16
|
+
/** The Graph APPLICATION permissions that read Teams channel messages TENANT-WIDE. See
|
|
17
|
+
* teams-graph-boundary.ts's header for why this is a denylist and what it can miss. */
|
|
18
|
+
export const TENANT_WIDE_CHANNEL_READ_ROLES: readonly string[] = ["ChannelMessage.Read.All", "Group.Read.All", "Group.ReadWrite.All"];
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* The `roles` claim of a JWT's payload, or `undefined` when the token does not parse as one.
|
|
22
|
+
*
|
|
23
|
+
* Returns an EMPTY array — not `undefined` — for a token that parses but carries no `roles` claim:
|
|
24
|
+
* that is a real, meaningful state (an app registration with no consented Graph application
|
|
25
|
+
* permissions), and it is exactly the per-team case. Only a genuine parse failure is `undefined`,
|
|
26
|
+
* which is what keeps "malformed" from being reported as "per-team". Pure.
|
|
27
|
+
*/
|
|
28
|
+
export function decodeJwtRoles(token: string): readonly string[] | undefined {
|
|
29
|
+
const parts = token.split(".");
|
|
30
|
+
if (parts.length !== 3 || parts[1].length === 0) return undefined;
|
|
31
|
+
let payload: unknown;
|
|
32
|
+
try {
|
|
33
|
+
payload = JSON.parse(Buffer.from(parts[1], "base64url").toString("utf-8"));
|
|
34
|
+
} catch {
|
|
35
|
+
return undefined;
|
|
36
|
+
}
|
|
37
|
+
// An ARRAY passes `typeof === "object"`, so it needs its own rejection: a JWT payload is a JSON
|
|
38
|
+
// object, and letting an array through would read its absent `roles` as [] — reporting "per-team"
|
|
39
|
+
// for a token that never parsed as a claims set at all.
|
|
40
|
+
if (typeof payload !== "object" || payload === null || Array.isArray(payload)) return undefined;
|
|
41
|
+
const roles = (payload as { roles?: unknown }).roles;
|
|
42
|
+
if (roles === undefined) return [];
|
|
43
|
+
if (!Array.isArray(roles)) return undefined;
|
|
44
|
+
return roles.filter((r): r is string => typeof r === "string");
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Every tenant-wide role the token actually carries (empty on the per-team path). Pure. */
|
|
48
|
+
export function tenantWideRolesIn(roles: readonly string[]): readonly string[] {
|
|
49
|
+
return TENANT_WIDE_CHANNEL_READ_ROLES.filter((r) => roles.includes(r));
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** The boundary a decoded `roles` claim implies — see teams-graph-boundary.ts's header for the one-sided reasoning.
|
|
53
|
+
* `undefined` roles (a token that did not parse) yield `"unknown"`, never a guess. Pure. */
|
|
54
|
+
export function boundaryFromRoles(roles: readonly string[] | undefined): TeamsReadBoundary {
|
|
55
|
+
if (roles === undefined) return "unknown";
|
|
56
|
+
return tenantWideRolesIn(roles).length > 0 ? "tenant-wide" : "per-team";
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** The boundary a minted Graph token implies. Pure; never logs or returns any part of the token. */
|
|
60
|
+
export function boundaryOfGraphToken(token: string): TeamsReadBoundary {
|
|
61
|
+
return boundaryFromRoles(decodeJwtRoles(token));
|
|
62
|
+
}
|
package/agent/ops.ts
CHANGED
|
@@ -5,7 +5,11 @@
|
|
|
5
5
|
// WHY THIS CANNOT BE AN ENV CHECK. Both boundaries use the SAME three env vars and the SAME token
|
|
6
6
|
// mint. The difference is which Graph permission the tenant consented, which lives in the token's
|
|
7
7
|
// `roles` claim — so the probe mints a token and decodes it. teams-graph-boundary.ts owns the
|
|
8
|
-
//
|
|
8
|
+
// reasoning and teams-graph-roles.ts the pure predicate; this file is only the ops skin over it.
|
|
9
|
+
//
|
|
10
|
+
// It imports core through the LIGHT door (`adapter-api-ops.js`), and the predicate from
|
|
11
|
+
// teams-graph-roles.ts, which imports no core (OPE-2219). The main barrel loads most of core, so one
|
|
12
|
+
// import of it here added about 1.9 s to the ops MCP server's start. Boundary rule 4d pins this.
|
|
9
13
|
//
|
|
10
14
|
// This file (not core) owns the Teams env-key knowledge — MICROSOFT_APP_ID / _PASSWORD /
|
|
11
15
|
// _TENANT_ID and their TEAMS_* fallbacks are the bundle's vendor fingerprints (boundary rule 6).
|
|
@@ -18,8 +22,8 @@
|
|
|
18
22
|
// path is a documented, supported configuration, and `missing` would make `ready` false and stop
|
|
19
23
|
// an operator who has made an informed choice. It is advice, exactly like the two-role DB nudge.
|
|
20
24
|
|
|
21
|
-
import { registerPreflightProbe, registerSurfaceCredentialProbe, type Check } from "@open-everywhere/core/agent/adapter-api.js";
|
|
22
|
-
import { boundaryOfGraphToken, TENANT_WIDE_CHANNEL_READ_ROLES, type TeamsReadBoundary } from "./channels/teams-graph-
|
|
25
|
+
import { registerPreflightProbe, registerSurfaceCredentialProbe, type Check } from "@open-everywhere/core/agent/adapter-api-ops.js";
|
|
26
|
+
import { boundaryOfGraphToken, TENANT_WIDE_CHANNEL_READ_ROLES, type TeamsReadBoundary } from "./channels/teams-graph-roles.js";
|
|
23
27
|
|
|
24
28
|
const TOKEN_LOGIN_BASE = "https://login.microsoftonline.com";
|
|
25
29
|
const GRAPH_TOKEN_SCOPE = "https://graph.microsoft.com/.default";
|
|
@@ -1,27 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
* - `tenant-wide` — Microsoft enforces NO team boundary; only OE's domain allowlist remains.
|
|
4
|
-
* - `unknown` — not determined yet, or the minted token did not parse. */
|
|
5
|
-
export type TeamsReadBoundary = "per-team" | "tenant-wide" | "unknown";
|
|
6
|
-
/** The Graph APPLICATION permissions that read Teams channel messages TENANT-WIDE. See the header
|
|
7
|
-
* for why this is a denylist and what it can miss. */
|
|
8
|
-
export declare const TENANT_WIDE_CHANNEL_READ_ROLES: readonly string[];
|
|
1
|
+
import { type TeamsReadBoundary } from "./teams-graph-roles.js";
|
|
2
|
+
export { boundaryFromRoles, boundaryOfGraphToken, decodeJwtRoles, TENANT_WIDE_CHANNEL_READ_ROLES, type TeamsReadBoundary, } from "./teams-graph-roles.js";
|
|
9
3
|
/** Where the denylist came from — quoted in the runbook so a future reader can re-verify it. */
|
|
10
4
|
export declare const TEAMS_READ_BOUNDARY_SOURCES: readonly string[];
|
|
11
|
-
/**
|
|
12
|
-
* The `roles` claim of a JWT's payload, or `undefined` when the token does not parse as one.
|
|
13
|
-
*
|
|
14
|
-
* Returns an EMPTY array — not `undefined` — for a token that parses but carries no `roles` claim:
|
|
15
|
-
* that is a real, meaningful state (an app registration with no consented Graph application
|
|
16
|
-
* permissions), and it is exactly the per-team case. Only a genuine parse failure is `undefined`,
|
|
17
|
-
* which is what keeps "malformed" from being reported as "per-team". Pure.
|
|
18
|
-
*/
|
|
19
|
-
export declare function decodeJwtRoles(token: string): readonly string[] | undefined;
|
|
20
|
-
/** The boundary a decoded `roles` claim implies — see the header for the one-sided reasoning.
|
|
21
|
-
* `undefined` roles (a token that did not parse) yield `"unknown"`, never a guess. Pure. */
|
|
22
|
-
export declare function boundaryFromRoles(roles: readonly string[] | undefined): TeamsReadBoundary;
|
|
23
|
-
/** The boundary a minted Graph token implies. Pure; never logs or returns any part of the token. */
|
|
24
|
-
export declare function boundaryOfGraphToken(token: string): TeamsReadBoundary;
|
|
25
5
|
/**
|
|
26
6
|
* The boundary last determined in THIS process, `"unknown"` before the first Graph token mint.
|
|
27
7
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"teams-graph-boundary.d.ts","sourceRoot":"","sources":["../../../agent/channels/teams-graph-boundary.ts"],"names":[],"mappings":"AA0DA
|
|
1
|
+
{"version":3,"file":"teams-graph-boundary.d.ts","sourceRoot":"","sources":["../../../agent/channels/teams-graph-boundary.ts"],"names":[],"mappings":"AA0DA,OAAO,EAAwD,KAAK,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAKtH,OAAO,EACL,iBAAiB,EACjB,oBAAoB,EACpB,cAAc,EACd,8BAA8B,EAC9B,KAAK,iBAAiB,GACvB,MAAM,wBAAwB,CAAC;AAEhC,gGAAgG;AAChG,eAAO,MAAM,2BAA2B,EAAE,SAAS,MAAM,EAIxD,CAAC;AAUF;;;;;;GAMG;AACH,wBAAgB,iBAAiB,IAAI,iBAAiB,CAErD;AAED,sDAAsD;AACtD,wBAAgB,8BAA8B,IAAI,IAAI,CAGrD;AAED;;;;;;;;GAQG;AACH,wBAAsB,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAwBrF"}
|
|
@@ -46,7 +46,8 @@
|
|
|
46
46
|
// 🔴 KNOWN LIMIT, STATED RATHER THAN HIDDEN: a denylist reports "per-team" for any FUTURE
|
|
47
47
|
// tenant-wide role Microsoft adds to those endpoints. An allowlist is not available (point 1 —
|
|
48
48
|
// there is nothing positive to match). Re-check the two API pages when Graph changes; the list
|
|
49
|
-
//
|
|
49
|
+
// in teams-graph-roles.ts (TENANT_WIDE_CHANNEL_READ_ROLES) is the whole of the claim, and
|
|
50
|
+
// TEAMS_READ_BOUNDARY_SOURCES below records where it came from.
|
|
50
51
|
//
|
|
51
52
|
// NO SIGNATURE VERIFICATION, ON PURPOSE. We decode a token WE minted, over TLS, from
|
|
52
53
|
// login.microsoftonline.com, seconds earlier. It is not attacker-supplied, and the decoded value
|
|
@@ -54,9 +55,11 @@
|
|
|
54
55
|
// need its JWKS and would buy nothing here. Nothing in this module logs, audits or returns any part
|
|
55
56
|
// of the token itself.
|
|
56
57
|
import { appendAudit, deploymentOrg } from "@open-everywhere/core/agent/adapter-api.js";
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
58
|
+
import { boundaryFromRoles, decodeJwtRoles, tenantWideRolesIn } from "./teams-graph-roles.js";
|
|
59
|
+
// The pure predicate lives in teams-graph-roles.ts, which imports nothing from core (OPE-2219). The
|
|
60
|
+
// ops arm needs only the predicate, and this file's audit row needs the main core barrel, which
|
|
61
|
+
// costs about 1.9 s to load. Re-exported here so this file's public names stay the same.
|
|
62
|
+
export { boundaryFromRoles, boundaryOfGraphToken, decodeJwtRoles, TENANT_WIDE_CHANNEL_READ_ROLES, } from "./teams-graph-roles.js";
|
|
60
63
|
/** Where the denylist came from — quoted in the runbook so a future reader can re-verify it. */
|
|
61
64
|
export const TEAMS_READ_BOUNDARY_SOURCES = [
|
|
62
65
|
"https://learn.microsoft.com/en-us/graph/api/channel-list-messages",
|
|
@@ -65,52 +68,6 @@ export const TEAMS_READ_BOUNDARY_SOURCES = [
|
|
|
65
68
|
];
|
|
66
69
|
/** The audit action that records a boundary determination. */
|
|
67
70
|
const TEAMS_READ_BOUNDARY_ACTION = "teams.read_boundary";
|
|
68
|
-
/**
|
|
69
|
-
* The `roles` claim of a JWT's payload, or `undefined` when the token does not parse as one.
|
|
70
|
-
*
|
|
71
|
-
* Returns an EMPTY array — not `undefined` — for a token that parses but carries no `roles` claim:
|
|
72
|
-
* that is a real, meaningful state (an app registration with no consented Graph application
|
|
73
|
-
* permissions), and it is exactly the per-team case. Only a genuine parse failure is `undefined`,
|
|
74
|
-
* which is what keeps "malformed" from being reported as "per-team". Pure.
|
|
75
|
-
*/
|
|
76
|
-
export function decodeJwtRoles(token) {
|
|
77
|
-
const parts = token.split(".");
|
|
78
|
-
if (parts.length !== 3 || parts[1].length === 0)
|
|
79
|
-
return undefined;
|
|
80
|
-
let payload;
|
|
81
|
-
try {
|
|
82
|
-
payload = JSON.parse(Buffer.from(parts[1], "base64url").toString("utf-8"));
|
|
83
|
-
}
|
|
84
|
-
catch {
|
|
85
|
-
return undefined;
|
|
86
|
-
}
|
|
87
|
-
// An ARRAY passes `typeof === "object"`, so it needs its own rejection: a JWT payload is a JSON
|
|
88
|
-
// object, and letting an array through would read its absent `roles` as [] — reporting "per-team"
|
|
89
|
-
// for a token that never parsed as a claims set at all.
|
|
90
|
-
if (typeof payload !== "object" || payload === null || Array.isArray(payload))
|
|
91
|
-
return undefined;
|
|
92
|
-
const roles = payload.roles;
|
|
93
|
-
if (roles === undefined)
|
|
94
|
-
return [];
|
|
95
|
-
if (!Array.isArray(roles))
|
|
96
|
-
return undefined;
|
|
97
|
-
return roles.filter((r) => typeof r === "string");
|
|
98
|
-
}
|
|
99
|
-
/** Every tenant-wide role the token actually carries (empty on the per-team path). Pure. */
|
|
100
|
-
function tenantWideRolesIn(roles) {
|
|
101
|
-
return TENANT_WIDE_CHANNEL_READ_ROLES.filter((r) => roles.includes(r));
|
|
102
|
-
}
|
|
103
|
-
/** The boundary a decoded `roles` claim implies — see the header for the one-sided reasoning.
|
|
104
|
-
* `undefined` roles (a token that did not parse) yield `"unknown"`, never a guess. Pure. */
|
|
105
|
-
export function boundaryFromRoles(roles) {
|
|
106
|
-
if (roles === undefined)
|
|
107
|
-
return "unknown";
|
|
108
|
-
return tenantWideRolesIn(roles).length > 0 ? "tenant-wide" : "per-team";
|
|
109
|
-
}
|
|
110
|
-
/** The boundary a minted Graph token implies. Pure; never logs or returns any part of the token. */
|
|
111
|
-
export function boundaryOfGraphToken(token) {
|
|
112
|
-
return boundaryFromRoles(decodeJwtRoles(token));
|
|
113
|
-
}
|
|
114
71
|
// --- the process-held determination + its audit row -------------------------------------------
|
|
115
72
|
let held = "unknown";
|
|
116
73
|
let heldRoles = [];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"teams-graph-boundary.js","sourceRoot":"","sources":["../../../agent/channels/teams-graph-boundary.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,EAAE;AACF,iGAAiG;AACjG,gGAAgG;AAChG,EAAE;AACF,mGAAmG;AACnG,4FAA4F;AAC5F,mGAAmG;AACnG,yEAAyE;AACzE,iGAAiG;AACjG,kGAAkG;AAClG,iFAAiF;AACjF,6EAA6E;AAC7E,EAAE;AACF,+FAA+F;AAC/F,iGAAiG;AACjG,EAAE;AACF,wFAAwF;AACxF,EAAE;AACF,kGAAkG;AAClG,iGAAiG;AACjG,mGAAmG;AACnG,8FAA8F;AAC9F,gGAAgG;AAChG,8FAA8F;AAC9F,8FAA8F;AAC9F,sDAAsD;AACtD,EAAE;AACF,iFAAiF;AACjF,EAAE;AACF,mGAAmG;AACnG,+FAA+F;AAC/F,oGAAoG;AACpG,0FAA0F;AAC1F,6FAA6F;AAC7F,2EAA2E;AAC3E,EAAE;AACF,mGAAmG;AACnG,gGAAgG;AAChG,qGAAqG;AACrG,iGAAiG;AACjG,+FAA+F;AAC/F,mGAAmG;AACnG,iGAAiG;AACjG,EAAE;AACF,0FAA0F;AAC1F,+FAA+F;AAC/F,+FAA+F;AAC/F
|
|
1
|
+
{"version":3,"file":"teams-graph-boundary.js","sourceRoot":"","sources":["../../../agent/channels/teams-graph-boundary.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/** Which boundary governs this deployment's Teams channel reads.
|
|
2
|
+
* - `per-team` — Microsoft enforces the team boundary (the RSC path, shipped default).
|
|
3
|
+
* - `tenant-wide` — Microsoft enforces NO team boundary; only OE's domain allowlist remains.
|
|
4
|
+
* - `unknown` — not determined yet, or the minted token did not parse. */
|
|
5
|
+
export type TeamsReadBoundary = "per-team" | "tenant-wide" | "unknown";
|
|
6
|
+
/** The Graph APPLICATION permissions that read Teams channel messages TENANT-WIDE. See
|
|
7
|
+
* teams-graph-boundary.ts's header for why this is a denylist and what it can miss. */
|
|
8
|
+
export declare const TENANT_WIDE_CHANNEL_READ_ROLES: readonly string[];
|
|
9
|
+
/**
|
|
10
|
+
* The `roles` claim of a JWT's payload, or `undefined` when the token does not parse as one.
|
|
11
|
+
*
|
|
12
|
+
* Returns an EMPTY array — not `undefined` — for a token that parses but carries no `roles` claim:
|
|
13
|
+
* that is a real, meaningful state (an app registration with no consented Graph application
|
|
14
|
+
* permissions), and it is exactly the per-team case. Only a genuine parse failure is `undefined`,
|
|
15
|
+
* which is what keeps "malformed" from being reported as "per-team". Pure.
|
|
16
|
+
*/
|
|
17
|
+
export declare function decodeJwtRoles(token: string): readonly string[] | undefined;
|
|
18
|
+
/** Every tenant-wide role the token actually carries (empty on the per-team path). Pure. */
|
|
19
|
+
export declare function tenantWideRolesIn(roles: readonly string[]): readonly string[];
|
|
20
|
+
/** The boundary a decoded `roles` claim implies — see teams-graph-boundary.ts's header for the one-sided reasoning.
|
|
21
|
+
* `undefined` roles (a token that did not parse) yield `"unknown"`, never a guess. Pure. */
|
|
22
|
+
export declare function boundaryFromRoles(roles: readonly string[] | undefined): TeamsReadBoundary;
|
|
23
|
+
/** The boundary a minted Graph token implies. Pure; never logs or returns any part of the token. */
|
|
24
|
+
export declare function boundaryOfGraphToken(token: string): TeamsReadBoundary;
|
|
25
|
+
//# sourceMappingURL=teams-graph-roles.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"teams-graph-roles.d.ts","sourceRoot":"","sources":["../../../agent/channels/teams-graph-roles.ts"],"names":[],"mappings":"AASA;;;+EAG+E;AAC/E,MAAM,MAAM,iBAAiB,GAAG,UAAU,GAAG,aAAa,GAAG,SAAS,CAAC;AAEvE;wFACwF;AACxF,eAAO,MAAM,8BAA8B,EAAE,SAAS,MAAM,EAAyE,CAAC;AAEtI;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,GAAG,SAAS,CAiB3E;AAED,4FAA4F;AAC5F,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,MAAM,EAAE,CAE7E;AAED;6FAC6F;AAC7F,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,GAAG,iBAAiB,CAGzF;AAED,oGAAoG;AACpG,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,iBAAiB,CAErE"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// THE PURE HALF of the Teams channel-read boundary predicate (OPE-428): decode a Graph token's
|
|
2
|
+
// `roles` claim and map it to a boundary. teams-graph-boundary.ts holds the reasoning (why a
|
|
3
|
+
// denylist, what it can miss) and the process-held determination with its audit row.
|
|
4
|
+
//
|
|
5
|
+
// 🔴 NO CORE IMPORT, ON PURPOSE (OPE-2219). The ops arm (agent/ops.ts) runs this predicate in the
|
|
6
|
+
// standalone ops MCP server. teams-graph-boundary.ts imports the main core barrel for its audit row,
|
|
7
|
+
// and that barrel loads most of core: about 1.9 s on a warm Windows machine. Boundary rule 4d
|
|
8
|
+
// (packages/core/scripts/boundary-test.ts) refuses an ops arm that reaches the main barrel.
|
|
9
|
+
/** The Graph APPLICATION permissions that read Teams channel messages TENANT-WIDE. See
|
|
10
|
+
* teams-graph-boundary.ts's header for why this is a denylist and what it can miss. */
|
|
11
|
+
export const TENANT_WIDE_CHANNEL_READ_ROLES = ["ChannelMessage.Read.All", "Group.Read.All", "Group.ReadWrite.All"];
|
|
12
|
+
/**
|
|
13
|
+
* The `roles` claim of a JWT's payload, or `undefined` when the token does not parse as one.
|
|
14
|
+
*
|
|
15
|
+
* Returns an EMPTY array — not `undefined` — for a token that parses but carries no `roles` claim:
|
|
16
|
+
* that is a real, meaningful state (an app registration with no consented Graph application
|
|
17
|
+
* permissions), and it is exactly the per-team case. Only a genuine parse failure is `undefined`,
|
|
18
|
+
* which is what keeps "malformed" from being reported as "per-team". Pure.
|
|
19
|
+
*/
|
|
20
|
+
export function decodeJwtRoles(token) {
|
|
21
|
+
const parts = token.split(".");
|
|
22
|
+
if (parts.length !== 3 || parts[1].length === 0)
|
|
23
|
+
return undefined;
|
|
24
|
+
let payload;
|
|
25
|
+
try {
|
|
26
|
+
payload = JSON.parse(Buffer.from(parts[1], "base64url").toString("utf-8"));
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return undefined;
|
|
30
|
+
}
|
|
31
|
+
// An ARRAY passes `typeof === "object"`, so it needs its own rejection: a JWT payload is a JSON
|
|
32
|
+
// object, and letting an array through would read its absent `roles` as [] — reporting "per-team"
|
|
33
|
+
// for a token that never parsed as a claims set at all.
|
|
34
|
+
if (typeof payload !== "object" || payload === null || Array.isArray(payload))
|
|
35
|
+
return undefined;
|
|
36
|
+
const roles = payload.roles;
|
|
37
|
+
if (roles === undefined)
|
|
38
|
+
return [];
|
|
39
|
+
if (!Array.isArray(roles))
|
|
40
|
+
return undefined;
|
|
41
|
+
return roles.filter((r) => typeof r === "string");
|
|
42
|
+
}
|
|
43
|
+
/** Every tenant-wide role the token actually carries (empty on the per-team path). Pure. */
|
|
44
|
+
export function tenantWideRolesIn(roles) {
|
|
45
|
+
return TENANT_WIDE_CHANNEL_READ_ROLES.filter((r) => roles.includes(r));
|
|
46
|
+
}
|
|
47
|
+
/** The boundary a decoded `roles` claim implies — see teams-graph-boundary.ts's header for the one-sided reasoning.
|
|
48
|
+
* `undefined` roles (a token that did not parse) yield `"unknown"`, never a guess. Pure. */
|
|
49
|
+
export function boundaryFromRoles(roles) {
|
|
50
|
+
if (roles === undefined)
|
|
51
|
+
return "unknown";
|
|
52
|
+
return tenantWideRolesIn(roles).length > 0 ? "tenant-wide" : "per-team";
|
|
53
|
+
}
|
|
54
|
+
/** The boundary a minted Graph token implies. Pure; never logs or returns any part of the token. */
|
|
55
|
+
export function boundaryOfGraphToken(token) {
|
|
56
|
+
return boundaryFromRoles(decodeJwtRoles(token));
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=teams-graph-roles.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"teams-graph-roles.js","sourceRoot":"","sources":["../../../agent/channels/teams-graph-roles.ts"],"names":[],"mappings":"AAAA,+FAA+F;AAC/F,6FAA6F;AAC7F,qFAAqF;AACrF,EAAE;AACF,kGAAkG;AAClG,qGAAqG;AACrG,8FAA8F;AAC9F,4FAA4F;AAQ5F;wFACwF;AACxF,MAAM,CAAC,MAAM,8BAA8B,GAAsB,CAAC,yBAAyB,EAAE,gBAAgB,EAAE,qBAAqB,CAAC,CAAC;AAEtI;;;;;;;GAOG;AACH,MAAM,UAAU,cAAc,CAAC,KAAa;IAC1C,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC/B,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IAClE,IAAI,OAAgB,CAAC;IACrB,IAAI,CAAC;QACH,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,WAAW,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC;IAC7E,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,gGAAgG;IAChG,kGAAkG;IAClG,wDAAwD;IACxD,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC;QAAE,OAAO,SAAS,CAAC;IAChG,MAAM,KAAK,GAAI,OAA+B,CAAC,KAAK,CAAC;IACrD,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,EAAE,CAAC;IACnC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IAC5C,OAAO,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAe,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC;AACjE,CAAC;AAED,4FAA4F;AAC5F,MAAM,UAAU,iBAAiB,CAAC,KAAwB;IACxD,OAAO,8BAA8B,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;AACzE,CAAC;AAED;6FAC6F;AAC7F,MAAM,UAAU,iBAAiB,CAAC,KAAoC;IACpE,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IAC1C,OAAO,iBAAiB,CAAC,KAAK,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,UAAU,CAAC;AAC1E,CAAC;AAED,oGAAoG;AACpG,MAAM,UAAU,oBAAoB,CAAC,KAAa;IAChD,OAAO,iBAAiB,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC,CAAC;AAClD,CAAC"}
|
package/dist/agent/ops.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { type Check } from "@open-everywhere/core/agent/adapter-api.js";
|
|
2
|
-
import { type TeamsReadBoundary } from "./channels/teams-graph-
|
|
1
|
+
import { type Check } from "@open-everywhere/core/agent/adapter-api-ops.js";
|
|
2
|
+
import { type TeamsReadBoundary } from "./channels/teams-graph-roles.js";
|
|
3
3
|
/** The check line for a determined boundary. `ok` only on the per-team path — that is the only one
|
|
4
4
|
* where Microsoft itself holds the line. */
|
|
5
5
|
export declare function boundaryCheck(boundary: TeamsReadBoundary): Check;
|
package/dist/agent/ops.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ops.d.ts","sourceRoot":"","sources":["../../agent/ops.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ops.d.ts","sourceRoot":"","sources":["../../agent/ops.ts"],"names":[],"mappings":"AAwBA,OAAO,EAA0D,KAAK,KAAK,EAAE,MAAM,gDAAgD,CAAC;AACpI,OAAO,EAAwD,KAAK,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AAmC/H;6CAC6C;AAC7C,wBAAgB,aAAa,CAAC,QAAQ,EAAE,iBAAiB,GAAG,KAAK,CA4BhE;AAED,wFAAwF;AACxF,wBAAsB,kBAAkB,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,GAAG,OAAO,CAAC,SAAS,KAAK,EAAE,CAAC,CA6B1F;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,IAAI,IAAI,CAGvC"}
|
package/dist/agent/ops.js
CHANGED
|
@@ -5,7 +5,11 @@
|
|
|
5
5
|
// WHY THIS CANNOT BE AN ENV CHECK. Both boundaries use the SAME three env vars and the SAME token
|
|
6
6
|
// mint. The difference is which Graph permission the tenant consented, which lives in the token's
|
|
7
7
|
// `roles` claim — so the probe mints a token and decodes it. teams-graph-boundary.ts owns the
|
|
8
|
-
//
|
|
8
|
+
// reasoning and teams-graph-roles.ts the pure predicate; this file is only the ops skin over it.
|
|
9
|
+
//
|
|
10
|
+
// It imports core through the LIGHT door (`adapter-api-ops.js`), and the predicate from
|
|
11
|
+
// teams-graph-roles.ts, which imports no core (OPE-2219). The main barrel loads most of core, so one
|
|
12
|
+
// import of it here added about 1.9 s to the ops MCP server's start. Boundary rule 4d pins this.
|
|
9
13
|
//
|
|
10
14
|
// This file (not core) owns the Teams env-key knowledge — MICROSOFT_APP_ID / _PASSWORD /
|
|
11
15
|
// _TENANT_ID and their TEAMS_* fallbacks are the bundle's vendor fingerprints (boundary rule 6).
|
|
@@ -17,8 +21,8 @@
|
|
|
17
21
|
// Teams lines). A mint that fails ⇒ one `info` line saying so. NEVER `missing`: the tenant-wide
|
|
18
22
|
// path is a documented, supported configuration, and `missing` would make `ready` false and stop
|
|
19
23
|
// an operator who has made an informed choice. It is advice, exactly like the two-role DB nudge.
|
|
20
|
-
import { registerPreflightProbe, registerSurfaceCredentialProbe } from "@open-everywhere/core/agent/adapter-api.js";
|
|
21
|
-
import { boundaryOfGraphToken, TENANT_WIDE_CHANNEL_READ_ROLES } from "./channels/teams-graph-
|
|
24
|
+
import { registerPreflightProbe, registerSurfaceCredentialProbe } from "@open-everywhere/core/agent/adapter-api-ops.js";
|
|
25
|
+
import { boundaryOfGraphToken, TENANT_WIDE_CHANNEL_READ_ROLES } from "./channels/teams-graph-roles.js";
|
|
22
26
|
const TOKEN_LOGIN_BASE = "https://login.microsoftonline.com";
|
|
23
27
|
const GRAPH_TOKEN_SCOPE = "https://graph.microsoft.com/.default";
|
|
24
28
|
/** A preflight must not hang on a slow or unreachable identity endpoint. */
|
package/dist/agent/ops.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ops.js","sourceRoot":"","sources":["../../agent/ops.ts"],"names":[],"mappings":"AAAA,oGAAoG;AACpG,oGAAoG;AACpG,0DAA0D;AAC1D,EAAE;AACF,kGAAkG;AAClG,kGAAkG;AAClG,8FAA8F;AAC9F,
|
|
1
|
+
{"version":3,"file":"ops.js","sourceRoot":"","sources":["../../agent/ops.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@open-everywhere/provider-teams",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "Open Everywhere Microsoft Teams provider — Teams channel and chat surfaces via Microsoft Graph.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agent",
|
|
@@ -73,6 +73,10 @@
|
|
|
73
73
|
"types": "./dist/agent/channels/teams-graph-boundary.d.ts",
|
|
74
74
|
"default": "./dist/agent/channels/teams-graph-boundary.js"
|
|
75
75
|
},
|
|
76
|
+
"./agent/channels/teams-graph-roles.js": {
|
|
77
|
+
"types": "./dist/agent/channels/teams-graph-roles.d.ts",
|
|
78
|
+
"default": "./dist/agent/channels/teams-graph-roles.js"
|
|
79
|
+
},
|
|
76
80
|
"./agent/channels/teams-graph-team-id.js": {
|
|
77
81
|
"types": "./dist/agent/channels/teams-graph-team-id.d.ts",
|
|
78
82
|
"default": "./dist/agent/channels/teams-graph-team-id.js"
|
|
@@ -155,7 +159,7 @@
|
|
|
155
159
|
"prepack": "npm run build",
|
|
156
160
|
"typecheck": "tsc --noEmit -p tsconfig.json",
|
|
157
161
|
"test": "npm run test:chain",
|
|
158
|
-
"test:chain": "
|
|
162
|
+
"test:chain": "tsx ../../../scripts/chain/run-chain.ts",
|
|
159
163
|
"test:teams": "NODE_NO_WARNINGS=1 tsx scripts/teams-test.ts",
|
|
160
164
|
"test:teams-connector": "NODE_NO_WARNINGS=1 tsx scripts/teams-connector-test.ts",
|
|
161
165
|
"test:teams-connector-seam": "NODE_NO_WARNINGS=1 tsx scripts/teams-connector-seam-test.ts",
|
|
@@ -172,7 +176,7 @@
|
|
|
172
176
|
"test:teams-spend-attribution": "NODE_NO_WARNINGS=1 tsx scripts/teams-spend-attribution-test.ts"
|
|
173
177
|
},
|
|
174
178
|
"dependencies": {
|
|
175
|
-
"@open-everywhere/core": "0.1.
|
|
179
|
+
"@open-everywhere/core": "0.1.5",
|
|
176
180
|
"zod": "4.5.4"
|
|
177
181
|
},
|
|
178
182
|
"peerDependencies": {
|