Skip to content
Merged
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
3 changes: 3 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ on:
pull_request:
branches: [main]

permissions:
contents: read

jobs:
test:
runs-on: ubuntu-latest
Expand Down
16 changes: 15 additions & 1 deletion packages/core/__tests__/core/sanitize.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { describe, expect, it } from "vitest";
import { sanitizeToolName } from "@execbox/core";
import { sanitizeIdentifier, sanitizeToolName } from "@execbox/core";

describe("sanitizeToolName", () => {
it("replaces punctuation and spaces with underscores", () => {
Expand All @@ -18,3 +18,17 @@ describe("sanitizeToolName", () => {
expect(sanitizeToolName("")).toBe("_");
});
});

describe("sanitizeIdentifier", () => {
it("removes leading and trailing replacement underscores", () => {
expect(sanitizeIdentifier(" ---tool name--- ")).toBe("tool_name");
});

it("preserves internal underscores while trimming the edges", () => {
expect(sanitizeIdentifier("tool---name")).toBe("tool_name");
});

it("falls back to an underscore when all characters are trimmed away", () => {
expect(sanitizeIdentifier("---")).toBe("_");
});
});
14 changes: 14 additions & 0 deletions packages/core/__tests__/repo/ciWorkflow.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { readFileSync } from "node:fs";
import { describe, expect, it } from "vitest";

describe("ci workflow", () => {
it("declares least-privilege GITHUB_TOKEN permissions", () => {
const workflow = readFileSync(
new URL("../../../../.github/workflows/ci.yml", import.meta.url),
"utf8",
);

expect(workflow).toContain("permissions:");
expect(workflow).toContain("contents: read");
});
});
26 changes: 21 additions & 5 deletions packages/core/src/identifier.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,23 @@ const RESERVED_WORDS = new Set([
"yield",
]);

const UNDERSCORE_CHAR_CODE = 95;

function trimEdgeUnderscores(value: string): string {
let start = 0;
let end = value.length;

while (start < end && value.charCodeAt(start) === UNDERSCORE_CHAR_CODE) {
start += 1;
}

while (end > start && value.charCodeAt(end - 1) === UNDERSCORE_CHAR_CODE) {
end -= 1;
}

return value.slice(start, end);
}

/**
* Returns whether the value is a valid JavaScript identifier.
*/
Expand Down Expand Up @@ -69,12 +86,11 @@ export function assertValidIdentifier(
* Converts a raw identifier-like value into a safe JavaScript identifier.
*/
export function sanitizeIdentifier(value: string): string {
const sanitized = value
.trim()
.replace(/[^A-Za-z0-9_$]+/g, "_")
.replace(/^_+|_+$/g, "");
const sanitized = value.trim().replace(/[^A-Za-z0-9_$]+/g, "_");

const trimmed = trimEdgeUnderscores(sanitized);

let safeName = sanitized.length > 0 ? sanitized : "_";
let safeName = trimmed.length > 0 ? trimmed : "_";

if (/^[0-9]/.test(safeName)) {
safeName = `_${safeName}`;
Expand Down
Loading