fix(notify): reject custom package notify owners
This commit is contained in:
@@ -111,6 +111,32 @@ describe("ensureBundledNotifyOwnership", () => {
|
||||
expect(readConfig(userConfigPath).plugin).toEqual(["team-notify-center", "oh-my-openagent", canonicalEntry])
|
||||
})
|
||||
|
||||
test("fails loudly for custom package-based notify plugin id", () => {
|
||||
// given
|
||||
const userConfigPath = join(userConfigDir, "opencode.json")
|
||||
writeFileSync(userConfigPath, JSON.stringify({ plugin: ["@custom/opencode-notify", "oh-my-openagent"] }, null, 2) + "\n")
|
||||
|
||||
// when
|
||||
const run = () => ensureBundledNotifyOwnership({ projectDirectory: projectDir, packageRoot })
|
||||
|
||||
// then
|
||||
expect(run).toThrow("Unsafe external notify plugin ownership detected")
|
||||
expect(readConfig(userConfigPath).plugin).toEqual(["@custom/opencode-notify", "oh-my-openagent"])
|
||||
})
|
||||
|
||||
test("fails loudly for custom tuple-based notify package id", () => {
|
||||
// given
|
||||
const userConfigPath = join(userConfigDir, "opencode.json")
|
||||
writeFileSync(userConfigPath, JSON.stringify({ plugin: [["npm:@custom/opencode-notify@1.2.3", {}], "oh-my-openagent"] }, null, 2) + "\n")
|
||||
|
||||
// when
|
||||
const run = () => ensureBundledNotifyOwnership({ projectDirectory: projectDir, packageRoot })
|
||||
|
||||
// then
|
||||
expect(run).toThrow("Unsafe external notify plugin ownership detected")
|
||||
expect(readConfig(userConfigPath).plugin).toEqual([["npm:@custom/opencode-notify@1.2.3", {}], "oh-my-openagent"])
|
||||
})
|
||||
|
||||
test("migrates stale bundled dist/opencode-notify file URL to canonical bundled entry", () => {
|
||||
// given
|
||||
const userConfigPath = join(userConfigDir, "opencode.json")
|
||||
|
||||
@@ -66,6 +66,35 @@ function isRecognizedExternalNotifyId(entry: string): boolean {
|
||||
return KNOWN_EXTERNAL_NOTIFY_IDS.some((base) => normalized === base || normalized.startsWith(`${base}@`))
|
||||
}
|
||||
|
||||
function stripNpmPrefix(entry: string): string {
|
||||
return entry.startsWith("npm:") ? entry.slice("npm:".length) : entry
|
||||
}
|
||||
|
||||
function stripVersionSuffix(entry: string): string {
|
||||
const trimmed = entry.trim()
|
||||
const versionSeparatorIndex = trimmed.lastIndexOf("@")
|
||||
if (versionSeparatorIndex <= 0) return trimmed
|
||||
|
||||
const slashIndex = trimmed.indexOf("/")
|
||||
if (trimmed.startsWith("@") && versionSeparatorIndex <= slashIndex) {
|
||||
return trimmed
|
||||
}
|
||||
|
||||
return trimmed.slice(0, versionSeparatorIndex)
|
||||
}
|
||||
|
||||
function isCustomPackageNotifyCandidate(entry: string): boolean {
|
||||
const normalized = stripNpmPrefix(entry.trim().toLowerCase())
|
||||
if (normalized.length === 0) return false
|
||||
if (normalized.includes("://")) return false
|
||||
|
||||
const packageIdentifier = stripVersionSuffix(normalized)
|
||||
if (packageIdentifier === "opencode-notify") return true
|
||||
if (/^@[a-z0-9._-]+\/opencode-notify$/i.test(packageIdentifier)) return true
|
||||
if (/^[a-z0-9._-]+\/opencode-notify$/i.test(packageIdentifier)) return true
|
||||
return false
|
||||
}
|
||||
|
||||
function normalizePathForComparison(pathValue: string): string {
|
||||
return resolve(pathValue).replace(/\\/g, "/").replace(/\/+$/, "")
|
||||
}
|
||||
@@ -133,6 +162,15 @@ function classifyPluginEntry(entry: OpenCodePluginEntry, index: number, canonica
|
||||
}
|
||||
}
|
||||
|
||||
if (isCustomPackageNotifyCandidate(entry)) {
|
||||
return {
|
||||
kind: "unsafe-external",
|
||||
entry,
|
||||
index,
|
||||
reason: "notify package entry is not an exact recognized kdco/notify identifier",
|
||||
}
|
||||
}
|
||||
|
||||
return { kind: "other", entry, index }
|
||||
}
|
||||
|
||||
@@ -172,6 +210,15 @@ function classifyPluginEntry(entry: OpenCodePluginEntry, index: number, canonica
|
||||
}
|
||||
}
|
||||
|
||||
if (isCustomPackageNotifyCandidate(tupleKey)) {
|
||||
return {
|
||||
kind: "unsafe-external",
|
||||
entry,
|
||||
index,
|
||||
reason: "notify package tuple entry is not an exact recognized kdco/notify identifier",
|
||||
}
|
||||
}
|
||||
|
||||
return { kind: "other", entry, index }
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user