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])
|
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", () => {
|
test("migrates stale bundled dist/opencode-notify file URL to canonical bundled entry", () => {
|
||||||
// given
|
// given
|
||||||
const userConfigPath = join(userConfigDir, "opencode.json")
|
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}@`))
|
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 {
|
function normalizePathForComparison(pathValue: string): string {
|
||||||
return resolve(pathValue).replace(/\\/g, "/").replace(/\/+$/, "")
|
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 }
|
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 }
|
return { kind: "other", entry, index }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user