Skip to content

Commit 02da612

Browse files
committed
fix(onboard): restrict messaging placeholder paths
1 parent fe0ffea commit 02da612

2 files changed

Lines changed: 29 additions & 6 deletions

File tree

src/lib/onboard/managed-startup-profile.test.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -759,6 +759,7 @@ describe("managed startup profile", () => {
759759
lines: [
760760
"SLACK_BOT_TOKEN=xoxb-OPENSHELL-RESOLVE-ENV-SLACK_BOT_TOKEN",
761761
"DISCORD_BOT_TOKEN=openshell:resolve:env:DISCORD_BOT_TOKEN",
762+
"TELEGRAM_BOT_TOKEN=openshell:resolve:env:v1_TELEGRAM_BOT_TOKEN",
762763
],
763764
templateRefs: ["credential.slackBotToken.placeholder"],
764765
},
@@ -776,6 +777,10 @@ describe("managed startup profile", () => {
776777
"a placeholder for a different environment key",
777778
"SLACK_BOT_TOKEN=openshell:resolve:env:DISCORD_BOT_TOKEN",
778779
],
780+
[
781+
"a versioned placeholder for a different environment key",
782+
"SLACK_BOT_TOKEN=openshell:resolve:env:v1_DISCORD_BOT_TOKEN",
783+
],
779784
])("rejects %s in messaging environment lines (#9355)", (_label, line) => {
780785
expect(() =>
781786
validateManagedStartupProfile({
@@ -821,6 +826,13 @@ describe("managed startup profile", () => {
821826
note: "SLACK_BOT_TOKEN=openshell:resolve:env:SLACK_BOT_TOKEN",
822827
},
823828
],
829+
[
830+
"a direct credential placeholder outside schema-owned fields",
831+
{
832+
...OPENCLAW_PROFILE.messaging.plan,
833+
note: "openshell:resolve:env:SLACK_BOT_TOKEN",
834+
},
835+
],
824836
])("rejects %s (#9355)", (_label, plan) => {
825837
expect(() =>
826838
validateManagedStartupProfile({

src/lib/onboard/managed-startup/profile.ts

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -994,13 +994,24 @@ function valueLooksLikeSecret(value: string): boolean {
994994
}
995995

996996
function isMessagingCredentialPlaceholder(path: readonly string[], value: unknown): boolean {
997-
return (
998-
path.length >= 2 &&
997+
if (typeof value !== "string" || !MESSAGING_CREDENTIAL_PLACEHOLDER_RE.test(value)) {
998+
return false;
999+
}
1000+
const isCredentialBindingPlaceholder =
1001+
path.length === 5 &&
9991002
path[0] === "messaging" &&
10001003
path[1] === "plan" &&
1001-
typeof value === "string" &&
1002-
MESSAGING_CREDENTIAL_PLACEHOLDER_RE.test(value)
1003-
);
1004+
path[2] === "credentialBindings" &&
1005+
JSON_ARRAY_INDEX_SEGMENT_RE.test(path[3] ?? "") &&
1006+
path[4] === "placeholder";
1007+
const isAgentRenderValuePlaceholder =
1008+
path.length >= 5 &&
1009+
path[0] === "messaging" &&
1010+
path[1] === "plan" &&
1011+
path[2] === "agentRender" &&
1012+
JSON_ARRAY_INDEX_SEGMENT_RE.test(path[3] ?? "") &&
1013+
path[4] === "value";
1014+
return isCredentialBindingPlaceholder || isAgentRenderValuePlaceholder;
10041015
}
10051016

10061017
function messagingCredentialPlaceholderEnvKey(value: string): string | null {
@@ -1503,7 +1514,7 @@ function assertPayloadStructureAndCredentialShapes(root: unknown): void {
15031514
const child = descriptor.value;
15041515
if (
15051516
isCredentialShapedName(key) &&
1506-
!isMessagingCredentialPlaceholder(current.path, child) &&
1517+
!isMessagingCredentialPlaceholder([...current.path, key], child) &&
15071518
!isMessagingPackagePin([...current.path, key], child)
15081519
) {
15091520
invalid(

0 commit comments

Comments
 (0)