From a8879430f0407bc12b120d9bc5efcff400ab6175 Mon Sep 17 00:00:00 2001 From: Tianyi Cui <53024+tianyicui@users.noreply.github.com> Date: Sat, 8 Aug 2026 14:20:06 +0800 Subject: [PATCH] ci: harden private repository link gate --- .../verify-public-repository-links.spec.ts | 21 ++++++++++++---- scripts/verify-public-repository-links.ts | 24 ++++++++++++++++++- 2 files changed, 40 insertions(+), 5 deletions(-) diff --git a/scripts/verify-public-repository-links.spec.ts b/scripts/verify-public-repository-links.spec.ts index 615bfa68e2..ec12f38427 100644 --- a/scripts/verify-public-repository-links.spec.ts +++ b/scripts/verify-public-repository-links.spec.ts @@ -2,18 +2,31 @@ import { describe, expect, it } from 'vitest' import { findInternalRepositoryReferences } from './verify-public-repository-links.ts' describe('public repository link policy', () => { - it('rejects internal repository references and accepts the public home', () => { + it('rejects encoded and case-varied internal identities without blocking public repositories', () => { const internalOwner = ['deepseek', 'harness'].join('-') const internalRepository = [internalOwner, internalOwner].join('/') + const encodedRepository = internalRepository.replaceAll('-', '%2D').replace('/', '%2F') + const htmlEncodedRepository = internalRepository.replace('/', '/') + const jsonEscapedRepository = internalRepository.replace('/', '\\/') + const unicodeEscapedRepository = internalRepository.replace('/', String.raw`\u002f`) const source = [ 'https://github.com/deepseek-ai/deepseek-harness-sdk', - `https://github.com/${internalRepository}/issues/1`, - `${internalOwner}#2`, + `https://github.com/${internalOwner}/cordis`, + `https://github.com/${internalRepository.toUpperCase()}/issues/1`, + `https://github.com/${encodedRepository}/issues/2`, + `https://github.com/${htmlEncodedRepository}/issues/3`, + `"https:\\/\\/github.com\\/${jsonEscapedRepository}\\/issues\\/4"`, + `"https:\\/\\/github.com\\/${unicodeEscapedRepository}\\/issues\\/5"`, + `${internalOwner.toUpperCase()}#6`, ].join('\n') expect(findInternalRepositoryReferences('subject.md', source)).toEqual([ - { file: 'subject.md', line: 2 }, { file: 'subject.md', line: 3 }, + { file: 'subject.md', line: 4 }, + { file: 'subject.md', line: 5 }, + { file: 'subject.md', line: 6 }, + { file: 'subject.md', line: 7 }, + { file: 'subject.md', line: 8 }, ]) }) }) diff --git a/scripts/verify-public-repository-links.ts b/scripts/verify-public-repository-links.ts index a57628e00c..6d1e537733 100644 --- a/scripts/verify-public-repository-links.ts +++ b/scripts/verify-public-repository-links.ts @@ -10,6 +10,27 @@ const internalOwner = ['deepseek', 'harness'].join('-') const internalRepository = [internalOwner, internalOwner].join('/') const internalIssueShorthand = `${internalOwner}#` +const namedReferenceCharacters: Readonly> = { + hyphen: '-', + num: '#', + sol: '/', +} + +/** Normalize source spellings that render or decode to repository separators. */ +function canonicalReferenceText(source: string): string { + return source + .replaceAll('\\/', '/') + .replace(/\\u(0023|002d|002f)/gi, (_match, code: string) => String.fromCodePoint(Number.parseInt(code, 16))) + .replace(/%(23|2d|2f)/gi, (_match, code: string) => String.fromCodePoint(Number.parseInt(code, 16))) + .replace(/&#(?:(\d+)|x([\da-f]+));/gi, (entity, decimal: string | undefined, hexadecimal: string | undefined) => { + const code = Number.parseInt(decimal ?? hexadecimal ?? '', decimal === undefined ? 16 : 10) + return code === 35 || code === 45 || code === 47 ? String.fromCodePoint(code) : entity + }) + .replace(/&(hyphen|num|sol);/gi, (entity, name: string) => namedReferenceCharacters[name.toLowerCase()] ?? entity) + .normalize('NFKC') + .toLowerCase() +} + /** One tracked reference to the internal repository. */ export interface InternalRepositoryReference { /** Repository-relative file path. */ @@ -27,7 +48,8 @@ export interface InternalRepositoryReference { export function findInternalRepositoryReferences(file: string, source: string): InternalRepositoryReference[] { const references: InternalRepositoryReference[] = [] for (const [index, line] of source.split('\n').entries()) { - if (line.includes(internalRepository) || line.includes(internalIssueShorthand)) { + const canonicalLine = canonicalReferenceText(line) + if (canonicalLine.includes(internalRepository) || canonicalLine.includes(internalIssueShorthand)) { references.push({ file, line: index + 1 }) } }