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
1,130 changes: 965 additions & 165 deletions src/data/schemas/v0/pipeline.ts

Large diffs are not rendered by default.

1,131 changes: 1,043 additions & 88 deletions src/data/schemas/v0/template.ts

Large diffs are not rendered by default.

13 changes: 13 additions & 0 deletions src/registry/extractors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -780,6 +780,19 @@ export const fmeListExtract = (raw: unknown): unknown => {
return raw;
};

/**
* IDP scorecard/check stats — normalizes timestamp to RFC3339 `time` for agent consumption.
* API: `{ name?, stats?, timestamp? }`
*/
export const scorecardStatsExtract = (raw: unknown): unknown => {
const r = raw as { name?: string; stats?: unknown[]; timestamp?: number | null };
return {
name: r.name,
stats: r.stats ?? [],
time: r.timestamp != null ? new Date(r.timestamp).toISOString() : "",
};
};

/** Extract FME feature flag single item — passthrough with trafficType.id flattened. */
export const fmeGetExtract = (raw: unknown): unknown => {
if (raw && typeof raw === "object") {
Expand Down
11 changes: 1 addition & 10 deletions src/registry/toolsets/idp.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { PathBuilderConfig, ToolsetDefinition } from "../types.js";
import { ngExtract, passthrough, v1ListExtract } from "../extractors.js";
import { ngExtract, passthrough, scorecardStatsExtract, v1ListExtract } from "../extractors.js";
import { parse as parseYaml } from "yaml";

const CONFIG_API_KEY = "__config_api_key";
Expand Down Expand Up @@ -38,15 +38,6 @@ const extractAuthParamRefs = (yamlStr: string): { apikeyRefs: string[]; apiKeySe
return { apikeyRefs, apiKeySecretRefs };
};

const scorecardStatsExtract = (raw: unknown): unknown => {
const r = raw as { name?: string; stats?: unknown[]; timestamp?: number | null };
return {
name: r.name,
stats: r.stats ?? [],
time: r.timestamp != null ? new Date(r.timestamp).toISOString() : "",
};
};

export const idpToolset: ToolsetDefinition = {
name: "idp",
displayName: "Internal Developer Portal",
Expand Down
34 changes: 34 additions & 0 deletions tests/coding-standards/agents-consistency.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/**
* Keep AGENTS.md aligned with docs/coding-standards.md and enforced architecture constants.
*/
import { describe, it, expect } from "vitest";
import { readFileSync } from "node:fs";
import { join } from "node:path";
import { ALLOWED_MCP_TOOLS } from "./constants.js";

const REPO_ROOT = join(import.meta.dirname, "../..");
const AGENTS_PATH = join(REPO_ROOT, "AGENTS.md");

describe("Coding standards — AGENTS.md consistency", () => {
const content = readFileSync(AGENTS_PATH, "utf8");

it("AGENTS.md documents 11 consolidated MCP tools including harness_schema", () => {
expect(content).toMatch(/11 consolidated tools/);
for (const tool of ALLOWED_MCP_TOOLS) {
expect(content, `missing ${tool} in AGENTS.md`).toContain(tool);
}
});

it("AGENTS.md references Zod v4 (not v3)", () => {
expect(content).toMatch(/Zod v4/);
expect(content).not.toMatch(/Zod v3/);
});

it("AGENTS.md documents pnpm standards:check guardrails", () => {
expect(content).toContain("pnpm standards:check");
});

it("AGENTS.md forbids console.log on stdout", () => {
expect(content).toMatch(/Use `console\.log\(\)` — stdout is the JSON-RPC transport/);
});
});
32 changes: 5 additions & 27 deletions tests/coding-standards/architecture.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,38 +13,16 @@ import { join, relative } from "node:path";
import { ALL_TOOLSET_NAMES } from "../../src/registry/index.js";
import type { ToolsetName } from "../../src/registry/types.js";

import { ALLOWED_MCP_TOOLS_SET, ALLOWED_REGISTER_TOOL_FILES } from "./constants.js";

const REPO_ROOT = join(import.meta.dirname, "../..");
const SRC = join(REPO_ROOT, "src");

/** The only MCP tools allowed in the server. */
const ALLOWED_MCP_TOOLS = new Set([
"harness_list",
"harness_get",
"harness_create",
"harness_update",
"harness_delete",
"harness_execute",
"harness_diagnose",
"harness_search",
"harness_describe",
"harness_status",
"harness_schema",
]);
const ALLOWED_MCP_TOOLS = ALLOWED_MCP_TOOLS_SET;

/** Only these files may call server.registerTool(). */
const ALLOWED_REGISTER_TOOL_FILES = new Set([
"src/tools/harness-list.ts",
"src/tools/harness-get.ts",
"src/tools/harness-create.ts",
"src/tools/harness-update.ts",
"src/tools/harness-delete.ts",
"src/tools/harness-execute.ts",
"src/tools/harness-diagnose.ts",
"src/tools/harness-search.ts",
"src/tools/harness-describe.ts",
"src/tools/harness-status.ts",
"src/tools/harness-schema.ts",
]);
const ALLOWED_REGISTER_TOOL_FILES_SET = new Set<string>(ALLOWED_REGISTER_TOOL_FILES);

/** Only these harness-*.ts handler files may exist under src/tools/. */
const ALLOWED_HARNESS_HANDLER_FILES = new Set([
Expand Down Expand Up @@ -210,7 +188,7 @@ describe("Coding standards — MCP tool handlers", () => {
if (!content.includes("registerTool")) continue;

const fileRel = rel(file);
if (!ALLOWED_REGISTER_TOOL_FILES.has(fileRel)) {
if (!ALLOWED_REGISTER_TOOL_FILES_SET.has(fileRel)) {
const tools = extractRegisterToolNames(content);
violations.push(`${fileRel} calls registerTool for: ${tools.join(", ") || "(dynamic)"}`);
}
Expand Down
35 changes: 35 additions & 0 deletions tests/coding-standards/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/**
* Single source of truth for architecture guardrail constants.
* Keep in sync with docs/coding-standards.md and AGENTS.md.
*/
export const ALLOWED_MCP_TOOLS = [
"harness_list",
"harness_get",
"harness_create",
"harness_update",
"harness_delete",
"harness_execute",
"harness_diagnose",
"harness_search",
"harness_describe",
"harness_status",
"harness_schema",
] as const;

export type AllowedMcpTool = (typeof ALLOWED_MCP_TOOLS)[number];

export const ALLOWED_MCP_TOOLS_SET = new Set<string>(ALLOWED_MCP_TOOLS);

export const ALLOWED_REGISTER_TOOL_FILES = [
"src/tools/harness-list.ts",
"src/tools/harness-get.ts",
"src/tools/harness-create.ts",
"src/tools/harness-update.ts",
"src/tools/harness-delete.ts",
"src/tools/harness-execute.ts",
"src/tools/harness-diagnose.ts",
"src/tools/harness-search.ts",
"src/tools/harness-describe.ts",
"src/tools/harness-status.ts",
"src/tools/harness-schema.ts",
] as const;
18 changes: 3 additions & 15 deletions tests/coding-standards/docs-consistency.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,17 @@ import { describe, it, expect } from "vitest";
import { readFileSync } from "node:fs";
import { join } from "node:path";

import { ALLOWED_MCP_TOOLS } from "./constants.js";

const REPO_ROOT = join(import.meta.dirname, "../..");
const STANDARDS_PATH = join(REPO_ROOT, "docs/coding-standards.md");

const REQUIRED_TOOLS = [
"harness_list",
"harness_get",
"harness_create",
"harness_update",
"harness_delete",
"harness_execute",
"harness_diagnose",
"harness_search",
"harness_describe",
"harness_status",
"harness_schema",
];

describe("Coding standards — documentation consistency", () => {
const content = readFileSync(STANDARDS_PATH, "utf8");

it("docs/coding-standards.md documents 11 consolidated MCP tools including harness_schema", () => {
expect(content).toMatch(/11 consolidated tool handlers/);
for (const tool of REQUIRED_TOOLS) {
for (const tool of ALLOWED_MCP_TOOLS) {
expect(content, `missing ${tool} in docs/coding-standards.md`).toContain(tool);
}
});
Expand Down
94 changes: 94 additions & 0 deletions tests/coding-standards/security.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/**
* Safety and security rules from docs/coding-standards.md §9.
*/
import { describe, it, expect } from "vitest";
import { readFileSync, readdirSync, statSync } from "node:fs";
import { join, relative } from "node:path";
import { Registry } from "../../src/registry/index.js";
import { idpToolset } from "../../src/registry/toolsets/idp.js";

const REPO_ROOT = join(import.meta.dirname, "../..");
const SRC = join(REPO_ROOT, "src");
const TOOLSET_DIR = join(SRC, "registry/toolsets");

const MINIMAL_CONFIG = {
HARNESS_API_KEY: "pat.testaccount.testtoken.testsecret",
HARNESS_BASE_URL: "https://app.harness.io",
} as const;

/** Patterns that must not appear in toolset response shaping (secret value leakage). */
const FORBIDDEN_SECRET_LEAK_PATTERNS: Array<{ pattern: RegExp; reason: string }> = [
{ pattern: /\bsecretValue\b/, reason: "secretValue field must not be mapped in toolsets" },
{ pattern: /\bencryptedSecret\b/, reason: "encryptedSecret must not be mapped in toolsets" },
{ pattern: /\bdecryptedValue\b/, reason: "decryptedValue must not be mapped in toolsets" },
];

function rel(path: string): string {
return relative(REPO_ROOT, path).replace(/\\/g, "/");
}

function walkTsFiles(dir: string): string[] {
const results: string[] = [];
for (const entry of readdirSync(dir)) {
const full = join(dir, entry);
const stat = statSync(full);
if (stat.isDirectory()) {
results.push(...walkTsFiles(full));
} else if (entry.endsWith(".ts")) {
results.push(full);
}
}
return results;
}

describe("Coding standards — security", () => {
it("secret resource descriptions state values are never exposed", () => {
const registry = new Registry(MINIMAL_CONFIG);
const secret = registry.getResource("secret");
expect(secret.description.toLowerCase()).toMatch(/never/);
expect(secret.operations.get?.description.toLowerCase()).toMatch(/never/);
expect(secret.operations.list?.description.toLowerCase()).toMatch(/never/);
});

it("toolset files do not map secret value fields in response extractors", () => {
const violations: string[] = [];

for (const file of walkTsFiles(TOOLSET_DIR)) {
const content = readFileSync(file, "utf8");
for (const { pattern, reason } of FORBIDDEN_SECRET_LEAK_PATTERNS) {
if (pattern.test(content)) {
violations.push(`${rel(file)}: ${reason}`);
}
}
}

expect(violations, violations.join("\n")).toEqual([]);
});

it("idp workflow execute bodyBuilder does not echo api_key_secret in the API request body", () => {
const workflow = idpToolset.resources.find((r) => r.resourceType === "idp_workflow");
const execute = workflow?.executeActions?.execute;
expect(execute?.bodyBuilder).toBeDefined();

const body = execute!.bodyBuilder!({
body: {
identifier: "wf-1",
workflow_details: {
identifier: "wf-1",
yaml: "spec:\n steps: []\n",
},
api_key_secret: "should-not-leak",
values: { foo: "bar" },
},
});

expect(body).toEqual({ identifier: "wf-1", values: { foo: "bar" } });
expect(body).not.toHaveProperty("api_key_secret");
});

it("HarnessClient enforces client-side rate limiting via RateLimiter", () => {
const content = readFileSync(join(SRC, "client/harness-client.ts"), "utf8");
expect(content).toContain("RateLimiter");
expect(content).toContain("rateLimiter.acquire()");
});
});
Loading