Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions packages/opencode/test/altimate/training-import.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,13 @@ function setupMocks(opts: {
saveSpy?.mockRestore()
budgetSpy?.mockRestore()

readFileSpy = spyOn(fs, "readFile").mockImplementation(async () => opts.fileContent)
readFileSpy = spyOn(fs, "readFile").mockImplementation((() => Promise.resolve(opts.fileContent)) as any)
countSpy = spyOn(TrainingStore, "count").mockImplementation(async () => ({
standard: opts.currentCount ?? 0,
glossary: opts.currentCount ?? 0,
playbook: opts.currentCount ?? 0,
naming: opts.currentCount ?? 0,
context: opts.currentCount ?? 0,
rule: opts.currentCount ?? 0,
pattern: opts.currentCount ?? 0,
}))
saveSpy = spyOn(TrainingStore, "save").mockImplementation(async () => {
Expand Down
48 changes: 48 additions & 0 deletions packages/opencode/test/file/path-traversal.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,54 @@ describe("Protected.isSensitiveWrite", () => {
expect(Protected.isSensitiveWrite("package.json")).toBeUndefined()
expect(Protected.isSensitiveWrite("models/schema.sql")).toBeUndefined()
})

test("detects private key and certificate extensions", () => {
expect(Protected.isSensitiveWrite("certs/server.pem")).toBe("server.pem")
expect(Protected.isSensitiveWrite("ssl/private.key")).toBe("private.key")
expect(Protected.isSensitiveWrite("auth/cert.p12")).toBe("cert.p12")
expect(Protected.isSensitiveWrite("deploy/signing.pfx")).toBe("signing.pfx")
})

test("detects remaining sensitive directories", () => {
expect(Protected.isSensitiveWrite(".gnupg/pubring.kbx")).toBe(".gnupg")
expect(Protected.isSensitiveWrite(".gcloud/credentials.db")).toBe(".gcloud")
expect(Protected.isSensitiveWrite(".kube/config")).toBe(".kube")
expect(Protected.isSensitiveWrite(".docker/config.json")).toBe(".docker")
})

test("detects remaining sensitive files", () => {
expect(Protected.isSensitiveWrite(".npmrc")).toBe(".npmrc")
expect(Protected.isSensitiveWrite(".pypirc")).toBe(".pypirc")
expect(Protected.isSensitiveWrite(".netrc")).toBe(".netrc")
expect(Protected.isSensitiveWrite(".htpasswd")).toBe(".htpasswd")
expect(Protected.isSensitiveWrite(".pgpass")).toBe(".pgpass")
expect(Protected.isSensitiveWrite("id_rsa")).toBe("id_rsa")
expect(Protected.isSensitiveWrite("id_ed25519")).toBe("id_ed25519")
})

test("detects sensitive files in subdirectories", () => {
expect(Protected.isSensitiveWrite("config/subdir/.npmrc")).toBe(".npmrc")
expect(Protected.isSensitiveWrite("deploy/certs/server.key")).toBe("server.key")
})

test("handles Windows backslash paths", () => {
expect(Protected.isSensitiveWrite(".git\\config")).toBe(".git")
expect(Protected.isSensitiveWrite("config\\.env")).toBe(".env")
expect(Protected.isSensitiveWrite(".ssh\\id_rsa")).toBe(".ssh")
})

test("detects .env variant files via startsWith check", () => {
expect(Protected.isSensitiveWrite(".env.production.local")).toBe(".env.production.local")
expect(Protected.isSensitiveWrite(".env.test")).toBe(".env.test")
expect(Protected.isSensitiveWrite(".env.custom")).toBe(".env.custom")
})

test("does not flag files that merely contain sensitive substrings", () => {
expect(Protected.isSensitiveWrite("src/git-helper.ts")).toBeUndefined()
expect(Protected.isSensitiveWrite("docs/env-setup.md")).toBeUndefined()
expect(Protected.isSensitiveWrite("lib/ssh-utils.js")).toBeUndefined()
expect(Protected.isSensitiveWrite("my-key-manager.ts")).toBeUndefined()
})
})

/*
Expand Down
67 changes: 67 additions & 0 deletions packages/opencode/test/id/id.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { describe, test, expect } from "bun:test"
import { Identifier } from "../../src/id/id"

describe("Identifier: monotonic ID generation and timestamp extraction", () => {
test("ascending IDs have correct prefix", () => {
const id = Identifier.ascending("session")
expect(id.startsWith("ses_")).toBe(true)

const toolId = Identifier.ascending("tool")
expect(toolId.startsWith("tool_")).toBe(true)

const msgId = Identifier.ascending("message")
expect(msgId.startsWith("msg_")).toBe(true)
})

test("descending IDs have correct prefix", () => {
const id = Identifier.descending("session")
expect(id.startsWith("ses_")).toBe(true)

const id2 = Identifier.descending("message")
expect(id2.startsWith("msg_")).toBe(true)
})

test("ascending IDs sort chronologically", () => {
const id1 = Identifier.create("tool", false, Date.now() - 10000)
const id2 = Identifier.create("tool", false, Date.now())
expect(id1 < id2).toBe(true)
})

test("descending IDs sort reverse-chronologically", () => {
const id1 = Identifier.create("tool", true, Date.now() - 10000)
const id2 = Identifier.create("tool", true, Date.now())
expect(id1 > id2).toBe(true)
})

test("timestamp() preserves ordering for ascending IDs", () => {
// timestamp() extracts a value from the lower 48 bits of the encoded ID.
// It doesn't round-trip to the exact millisecond, but it preserves ordering,
// which is what Truncate.cleanup() relies on for age comparisons.
const t1 = Date.now() - 60000
const t2 = Date.now()
const id1 = Identifier.create("session", false, t1)
const id2 = Identifier.create("session", false, t2)
expect(Identifier.timestamp(id1)).toBeLessThan(Identifier.timestamp(id2))
})

test("given ID is returned if prefix matches", () => {
const existing = "ses_abc123"
const result = Identifier.ascending("session", existing)
expect(result).toBe(existing)
})

test("given ID throws if prefix mismatches", () => {
expect(() => Identifier.ascending("session", "msg_abc123")).toThrow(
"does not start with ses",
)
})

test("monotonic counter differentiates same-millisecond IDs", () => {
const ts = Date.now()
const id1 = Identifier.create("tool", false, ts)
const id2 = Identifier.create("tool", false, ts)
expect(id1).not.toBe(id2)
// Both ascending, counter increments, so id1 < id2
expect(id1 < id2).toBe(true)
})
})
Loading