+79
| name: GitPin Evidence Gate | ||
| description: Verify that material PR claims cover the actual diff and point to exact committed evidence. | ||
| author: Sarosh Hussain | ||
| branding: | ||
| icon: check-square | ||
| color: green | ||
| inputs: | ||
| base-sha: | ||
| description: Full base commit SHA from github.event.pull_request.base.sha. | ||
| required: true | ||
| head-sha: | ||
| description: Full head commit SHA from github.event.pull_request.head.sha. | ||
| required: true | ||
| policy-path: | ||
| description: Trusted gate policy path read from the base commit. | ||
| required: false | ||
| default: .gitpin/gate.yml | ||
| repository-name: | ||
| description: Name used in generated full-SHA citations. | ||
| required: false | ||
| default: "" | ||
| gitpin-version: | ||
| description: Exact npm version to execute. Override only from a trusted workflow. | ||
| required: false | ||
| default: 0.6.0 | ||
| outputs: | ||
| report-path: | ||
| description: Path to the deterministic JSON gate report. | ||
| value: ${{ steps.gate.outputs.report-path }} | ||
| report-id: | ||
| description: Stable identifier for this gate result. | ||
| value: ${{ steps.gate.outputs.report-id }} | ||
| status: | ||
| description: ok or failed. | ||
| value: ${{ steps.gate.outputs.status }} | ||
| runs: | ||
| using: composite | ||
| steps: | ||
| - name: Set up Node.js | ||
| uses: actions/setup-node@v7 | ||
| with: | ||
| node-version: 24 | ||
| - name: Verify PR evidence | ||
| id: gate | ||
| shell: bash | ||
| env: | ||
| BASE_SHA: ${{ inputs.base-sha }} | ||
| HEAD_SHA: ${{ inputs.head-sha }} | ||
| POLICY_PATH: ${{ inputs.policy-path }} | ||
| REPOSITORY_NAME: ${{ inputs.repository-name }} | ||
| GITPIN_VERSION: ${{ inputs.gitpin-version }} | ||
| NPM_CONFIG_REGISTRY: https://registry.npmjs.org | ||
| run: | | ||
| set -uo pipefail | ||
| REPORT_PATH="${RUNNER_TEMP}/gitpin-gate-report.json" | ||
| REPOSITORY_NAME="${REPOSITORY_NAME:-${GITHUB_REPOSITORY:-repository}}" | ||
| EMPTY_NPMRC="${RUNNER_TEMP}/gitpin-empty.npmrc" | ||
| : > "$EMPTY_NPMRC" | ||
| cd "$RUNNER_TEMP" | ||
| set +e | ||
| npm exec --yes \ | ||
| --registry="https://registry.npmjs.org" \ | ||
| --userconfig="$EMPTY_NPMRC" \ | ||
| --package="gitpin@${GITPIN_VERSION}" -- gitpin gate \ | ||
| --root "$GITHUB_WORKSPACE" \ | ||
| --base "$BASE_SHA" \ | ||
| --head "$HEAD_SHA" \ | ||
| --policy "$POLICY_PATH" \ | ||
| --repository "$REPOSITORY_NAME" > "$REPORT_PATH" | ||
| GATE_EXIT=$? | ||
| set -e | ||
| echo "report-path=$REPORT_PATH" >> "$GITHUB_OUTPUT" | ||
| node "${GITHUB_ACTION_PATH}/scripts/render-gate-action-report.mjs" "$REPORT_PATH" | ||
| exit "$GATE_EXIT" |
| export declare function runGateCommand(options: string[]): Promise<import("./gate-types").GateReport>; |
| "use strict"; | ||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||
| exports.runGateCommand = runGateCommand; | ||
| const gate_1 = require("./gate"); | ||
| async function runGateCommand(options) { | ||
| let base; | ||
| let head; | ||
| let root; | ||
| let repository; | ||
| let policyPath; | ||
| for (let index = 0; index < options.length; index += 1) { | ||
| const option = options[index]; | ||
| const value = options[index + 1]; | ||
| if (!value || value.startsWith('--')) | ||
| throw new Error(`Option ${option} requires a value.`); | ||
| switch (option) { | ||
| case '--base': | ||
| base = value; | ||
| break; | ||
| case '--head': | ||
| head = value; | ||
| break; | ||
| case '--root': | ||
| root = value; | ||
| break; | ||
| case '--repository': | ||
| repository = value; | ||
| break; | ||
| case '--policy': | ||
| policyPath = value; | ||
| break; | ||
| default: | ||
| throw new Error(`Unknown gate option: ${option}. Run "gitpin help" for usage.`); | ||
| } | ||
| index += 1; | ||
| } | ||
| if (!base || !head) | ||
| throw new Error('gate requires --base <full-sha> and --head <full-sha>.'); | ||
| return (0, gate_1.runGitPinGate)({ | ||
| base, | ||
| head, | ||
| ...(root ? { root } : {}), | ||
| ...(repository ? { repository } : {}), | ||
| ...(policyPath ? { policyPath } : {}), | ||
| }); | ||
| } | ||
| //# sourceMappingURL=cli-gate.js.map |
| {"version":3,"file":"cli-gate.js","sourceRoot":"","sources":["../src/cli-gate.ts"],"names":[],"mappings":";;;AAAA,iCAAuC;AAEhC,KAAK,yBAAyB,OAAiB;IACpD,IAAI,IAAwB,CAAC;IAC7B,IAAI,IAAwB,CAAC;IAC7B,IAAI,IAAwB,CAAC;IAC7B,IAAI,UAA8B,CAAC;IACnC,IAAI,UAA8B,CAAC;IAEnC,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,OAAO,CAAC,MAAM,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;QACvD,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;QAC9B,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;QACjC,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,UAAU,MAAM,oBAAoB,CAAC,CAAC;QAC5F,QAAQ,MAAM,EAAE,CAAC;YACf,KAAK,QAAQ;gBACX,IAAI,GAAG,KAAK,CAAC;gBACb,MAAM;YACR,KAAK,QAAQ;gBACX,IAAI,GAAG,KAAK,CAAC;gBACb,MAAM;YACR,KAAK,QAAQ;gBACX,IAAI,GAAG,KAAK,CAAC;gBACb,MAAM;YACR,KAAK,cAAc;gBACjB,UAAU,GAAG,KAAK,CAAC;gBACnB,MAAM;YACR,KAAK,UAAU;gBACb,UAAU,GAAG,KAAK,CAAC;gBACnB,MAAM;YACR;gBACE,MAAM,IAAI,KAAK,CAAC,wBAAwB,MAAM,gCAAgC,CAAC,CAAC;QACpF,CAAC;QACD,KAAK,IAAI,CAAC,CAAC;IACb,CAAC;IACD,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI;QAAE,MAAM,IAAI,KAAK,CAAC,wDAAwD,CAAC,CAAC;IAC9F,OAAO,IAAA,oBAAa,EAAC;QACnB,IAAI;QACJ,IAAI;QACJ,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACzB,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACrC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACtC,CAAC,CAAC;AACL,CAAC"} |
| import { type ChangeEvidence, type GatePolicy } from './gate-types'; | ||
| export declare function resolveCommit(root: string, revision: string, label: string): string; | ||
| export declare function mergeBase(root: string, baseSha: string, headSha: string): string; | ||
| export declare function readPolicyAtBase(root: string, baseSha: string, policyPath: string): { | ||
| raw: string; | ||
| policy: GatePolicy; | ||
| }; | ||
| export declare function readManifestAtHead(root: string, headSha: string, manifestPath: string, maxBytes: number): { | ||
| raw: string; | ||
| manifest: ChangeEvidence; | ||
| }; | ||
| export declare function changedPaths(root: string, fromSha: string, headSha: string, limit: number): string[]; | ||
| export declare function readGitFile(root: string, sha: string, sourcePath: string, maxBytes: number): string; | ||
| export declare function sha256(value: string): string; |
| "use strict"; | ||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||
| exports.resolveCommit = resolveCommit; | ||
| exports.mergeBase = mergeBase; | ||
| exports.readPolicyAtBase = readPolicyAtBase; | ||
| exports.readManifestAtHead = readManifestAtHead; | ||
| exports.changedPaths = changedPaths; | ||
| exports.readGitFile = readGitFile; | ||
| exports.sha256 = sha256; | ||
| const node_child_process_1 = require("node:child_process"); | ||
| const node_crypto_1 = require("node:crypto"); | ||
| const yaml_1 = require("yaml"); | ||
| const gate_types_1 = require("./gate-types"); | ||
| function resolveCommit(root, revision, label) { | ||
| if (!/^[0-9a-f]{40}$/iu.test(revision)) | ||
| throw new Error(`${label} must be a full 40-character Git SHA.`); | ||
| try { | ||
| return gitText(root, ['rev-parse', '--verify', `${revision}^{commit}`], 1024) | ||
| .trim() | ||
| .toLowerCase(); | ||
| } | ||
| catch { | ||
| throw new Error(`${label} commit ${revision} is unavailable.`); | ||
| } | ||
| } | ||
| function mergeBase(root, baseSha, headSha) { | ||
| try { | ||
| return gitText(root, ['merge-base', baseSha, headSha], 1024).trim().toLowerCase(); | ||
| } | ||
| catch { | ||
| throw new Error(`No merge base exists between ${baseSha} and ${headSha}.`); | ||
| } | ||
| } | ||
| function readPolicyAtBase(root, baseSha, policyPath) { | ||
| const raw = readGitText(root, baseSha, policyPath, 1024 * 1024); | ||
| let parsed; | ||
| try { | ||
| parsed = (0, yaml_1.parse)(raw); | ||
| } | ||
| catch (error) { | ||
| throw new Error(`Trusted gate policy ${policyPath} is invalid YAML: ${errorMessage(error)}`); | ||
| } | ||
| const result = gate_types_1.gatePolicySchema.safeParse(parsed); | ||
| if (!result.success) | ||
| throw new Error(`Trusted gate policy ${policyPath} is invalid: ${result.error.message}`); | ||
| return { raw, policy: result.data }; | ||
| } | ||
| function readManifestAtHead(root, headSha, manifestPath, maxBytes) { | ||
| const raw = readGitText(root, headSha, manifestPath, maxBytes); | ||
| let parsed; | ||
| try { | ||
| parsed = JSON.parse(raw); | ||
| } | ||
| catch (error) { | ||
| throw new Error(`Evidence manifest ${manifestPath} is invalid JSON: ${errorMessage(error)}`); | ||
| } | ||
| const result = gate_types_1.changeEvidenceSchema.safeParse(parsed); | ||
| if (!result.success) | ||
| throw new Error(`Evidence manifest ${manifestPath} is invalid: ${result.error.message}`); | ||
| return { raw, manifest: result.data }; | ||
| } | ||
| function changedPaths(root, fromSha, headSha, limit) { | ||
| const output = gitText(root, ['diff', '--name-only', '--no-renames', '-z', fromSha, headSha, '--'], 10 * 1024 * 1024); | ||
| const paths = output.split('\0').filter(Boolean).sort(); | ||
| if (paths.length > limit) | ||
| throw new Error(`Change has ${paths.length} paths; trusted policy limit is ${limit}.`); | ||
| return paths; | ||
| } | ||
| function readGitFile(root, sha, sourcePath, maxBytes) { | ||
| return readGitText(root, sha, sourcePath, maxBytes); | ||
| } | ||
| function sha256(value) { | ||
| return (0, node_crypto_1.createHash)('sha256').update(value, 'utf8').digest('hex'); | ||
| } | ||
| function readGitText(root, sha, sourcePath, maxBytes) { | ||
| try { | ||
| const value = gitText(root, ['show', `${sha}:${sourcePath}`], maxBytes); | ||
| if (Buffer.byteLength(value, 'utf8') > maxBytes) | ||
| throw new Error('file exceeds byte limit'); | ||
| return value; | ||
| } | ||
| catch (error) { | ||
| throw new Error(`Cannot read ${sourcePath} at ${sha}: ${errorMessage(error)}`); | ||
| } | ||
| } | ||
| function gitText(root, args, maxBuffer) { | ||
| return (0, node_child_process_1.execFileSync)('git', ['--no-replace-objects', ...args], { | ||
| cwd: root, | ||
| encoding: 'utf8', | ||
| env: { ...process.env, GIT_NO_REPLACE_OBJECTS: '1' }, | ||
| maxBuffer, | ||
| windowsHide: true, | ||
| }); | ||
| } | ||
| function errorMessage(error) { | ||
| return error instanceof Error ? error.message : String(error); | ||
| } | ||
| //# sourceMappingURL=gate-policy.js.map |
| {"version":3,"file":"gate-policy.js","sourceRoot":"","sources":["../src/gate-policy.ts"],"names":[],"mappings":"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"} |
| import { z } from 'zod'; | ||
| declare const evidenceLocatorSchema: z.ZodObject<{ | ||
| ref: z.ZodEnum<{ | ||
| base: "base"; | ||
| head: "head"; | ||
| }>; | ||
| path: z.ZodString; | ||
| lineStart: z.ZodNumber; | ||
| lineEnd: z.ZodNumber; | ||
| contentSha256: z.ZodString; | ||
| }, z.core.$strict>; | ||
| export declare const changeEvidenceSchema: z.ZodObject<{ | ||
| schemaVersion: z.ZodLiteral<1>; | ||
| summary: z.ZodString; | ||
| claims: z.ZodArray<z.ZodObject<{ | ||
| id: z.ZodString; | ||
| statement: z.ZodString; | ||
| covers: z.ZodArray<z.ZodString>; | ||
| evidence: z.ZodArray<z.ZodObject<{ | ||
| ref: z.ZodEnum<{ | ||
| base: "base"; | ||
| head: "head"; | ||
| }>; | ||
| path: z.ZodString; | ||
| lineStart: z.ZodNumber; | ||
| lineEnd: z.ZodNumber; | ||
| contentSha256: z.ZodString; | ||
| }, z.core.$strict>>; | ||
| }, z.core.$strict>>; | ||
| }, z.core.$strict>; | ||
| export declare const gatePolicySchema: z.ZodObject<{ | ||
| schemaVersion: z.ZodLiteral<1>; | ||
| manifestPath: z.ZodDefault<z.ZodString>; | ||
| coverage: z.ZodDefault<z.ZodObject<{ | ||
| include: z.ZodDefault<z.ZodArray<z.ZodString>>; | ||
| exclude: z.ZodDefault<z.ZodArray<z.ZodString>>; | ||
| }, z.core.$strict>>; | ||
| policyChanges: z.ZodDefault<z.ZodEnum<{ | ||
| allow: "allow"; | ||
| block: "block"; | ||
| }>>; | ||
| limits: z.ZodDefault<z.ZodObject<{ | ||
| changedPaths: z.ZodDefault<z.ZodNumber>; | ||
| claims: z.ZodDefault<z.ZodNumber>; | ||
| evidencePerClaim: z.ZodDefault<z.ZodNumber>; | ||
| fileBytes: z.ZodDefault<z.ZodNumber>; | ||
| }, z.core.$strict>>; | ||
| }, z.core.$strict>; | ||
| export type ChangeEvidence = z.infer<typeof changeEvidenceSchema>; | ||
| export type GatePolicy = z.infer<typeof gatePolicySchema>; | ||
| export type EvidenceLocator = z.infer<typeof evidenceLocatorSchema>; | ||
| export interface GateViolation { | ||
| code: string; | ||
| message: string; | ||
| path?: string; | ||
| claimId?: string; | ||
| } | ||
| export interface VerifiedLocator { | ||
| ref: 'base' | 'head'; | ||
| path: string; | ||
| lineStart: number; | ||
| lineEnd: number; | ||
| expectedContentSha256: string; | ||
| actualContentSha256: string | null; | ||
| status: 'verified' | 'failed'; | ||
| citation: string | null; | ||
| handle: string | null; | ||
| } | ||
| export interface GateClaimReport { | ||
| id: string; | ||
| statement: string; | ||
| status: 'evidence-verified' | 'failed'; | ||
| covers: string[]; | ||
| evidence: VerifiedLocator[]; | ||
| } | ||
| export interface GateReport { | ||
| kind: 'gitpin-gate-report'; | ||
| schemaVersion: 1; | ||
| status: 'ok' | 'failed'; | ||
| reportId: string; | ||
| repository: string; | ||
| baseSha: string; | ||
| headSha: string; | ||
| mergeBaseSha: string; | ||
| policy: { | ||
| path: string; | ||
| sha256: string; | ||
| }; | ||
| manifest: { | ||
| path: string; | ||
| sha256: string; | ||
| }; | ||
| changedPaths: { | ||
| all: string[]; | ||
| required: string[]; | ||
| uncovered: string[]; | ||
| }; | ||
| claims: GateClaimReport[]; | ||
| violations: GateViolation[]; | ||
| message: string; | ||
| } | ||
| export declare function isSafeRelativePath(value: string): boolean; | ||
| export {}; |
| "use strict"; | ||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||
| exports.gatePolicySchema = exports.changeEvidenceSchema = void 0; | ||
| exports.isSafeRelativePath = isSafeRelativePath; | ||
| const zod_1 = require("zod"); | ||
| const relativePath = zod_1.z | ||
| .string() | ||
| .min(1) | ||
| .max(500) | ||
| .refine((value) => isSafeRelativePath(value), 'Must be a normalized repository-relative path.'); | ||
| const evidenceLocatorSchema = zod_1.z | ||
| .object({ | ||
| ref: zod_1.z.enum(['base', 'head']), | ||
| path: relativePath, | ||
| lineStart: zod_1.z.number().int().positive(), | ||
| lineEnd: zod_1.z.number().int().positive(), | ||
| contentSha256: zod_1.z.string().regex(/^[0-9a-f]{64}$/u), | ||
| }) | ||
| .strict() | ||
| .refine((value) => value.lineEnd >= value.lineStart, 'lineEnd must be greater than or equal to lineStart.'); | ||
| const claimSchema = zod_1.z | ||
| .object({ | ||
| id: zod_1.z.string().regex(/^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/u), | ||
| statement: zod_1.z.string().min(1).max(1000), | ||
| covers: zod_1.z.array(relativePath).min(1).max(100), | ||
| evidence: zod_1.z.array(evidenceLocatorSchema).min(1).max(16), | ||
| }) | ||
| .strict(); | ||
| exports.changeEvidenceSchema = zod_1.z | ||
| .object({ | ||
| schemaVersion: zod_1.z.literal(1), | ||
| summary: zod_1.z.string().min(1).max(2000), | ||
| claims: zod_1.z.array(claimSchema).max(100), | ||
| }) | ||
| .strict(); | ||
| exports.gatePolicySchema = zod_1.z | ||
| .object({ | ||
| schemaVersion: zod_1.z.literal(1), | ||
| manifestPath: relativePath.default('.gitpin/change-evidence.json'), | ||
| coverage: zod_1.z | ||
| .object({ | ||
| include: zod_1.z.array(zod_1.z.string().min(1).max(500)).min(1).default(['**']), | ||
| exclude: zod_1.z.array(zod_1.z.string().min(1).max(500)).default([]), | ||
| }) | ||
| .strict() | ||
| .default({ include: ['**'], exclude: [] }), | ||
| policyChanges: zod_1.z.enum(['block', 'allow']).default('block'), | ||
| limits: zod_1.z | ||
| .object({ | ||
| changedPaths: zod_1.z.number().int().min(1).max(1000).default(100), | ||
| claims: zod_1.z.number().int().min(1).max(100).default(50), | ||
| evidencePerClaim: zod_1.z.number().int().min(1).max(16).default(8), | ||
| fileBytes: zod_1.z | ||
| .number() | ||
| .int() | ||
| .min(1024) | ||
| .max(10 * 1024 * 1024) | ||
| .default(2 * 1024 * 1024), | ||
| }) | ||
| .strict() | ||
| .default({ changedPaths: 100, claims: 50, evidencePerClaim: 8, fileBytes: 2 * 1024 * 1024 }), | ||
| }) | ||
| .strict(); | ||
| function isSafeRelativePath(value) { | ||
| if (!value || value.includes('\0') || value.includes('\\') || value.startsWith('/') || /^[A-Za-z]:/u.test(value)) { | ||
| return false; | ||
| } | ||
| const segments = value.split('/'); | ||
| return !segments.some((segment) => segment === '' || segment === '.' || segment === '..'); | ||
| } | ||
| //# sourceMappingURL=gate-types.js.map |
| {"version":3,"file":"gate-types.js","sourceRoot":"","sources":["../src/gate-types.ts"],"names":[],"mappings":"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"} |
| import { type GateReport } from './gate-types'; | ||
| export declare function runGitPinGate(input: { | ||
| root?: string; | ||
| repository?: string; | ||
| base: string; | ||
| head: string; | ||
| policyPath?: string; | ||
| }): Promise<GateReport>; |
+217
| "use strict"; | ||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||
| exports.runGitPinGate = runGitPinGate; | ||
| const node_crypto_1 = require("node:crypto"); | ||
| const node_path_1 = require("node:path"); | ||
| const evidence_citation_1 = require("./evidence-citation"); | ||
| const policy_1 = require("./policy"); | ||
| const gate_policy_1 = require("./gate-policy"); | ||
| const gate_types_1 = require("./gate-types"); | ||
| const DEFAULT_POLICY_PATH = '.gitpin/gate.yml'; | ||
| async function runGitPinGate(input) { | ||
| const root = (0, node_path_1.resolve)(input.root ?? process.cwd()); | ||
| const repository = input.repository ?? (0, node_path_1.basename)(root); | ||
| const policyPath = input.policyPath ?? DEFAULT_POLICY_PATH; | ||
| if (!(0, gate_types_1.isSafeRelativePath)(policyPath)) | ||
| throw new Error('--policy must be a normalized repository-relative path.'); | ||
| const baseSha = (0, gate_policy_1.resolveCommit)(root, input.base, '--base'); | ||
| const headSha = (0, gate_policy_1.resolveCommit)(root, input.head, '--head'); | ||
| const mergeBaseSha = (0, gate_policy_1.mergeBase)(root, baseSha, headSha); | ||
| const policyResult = (0, gate_policy_1.readPolicyAtBase)(root, baseSha, policyPath); | ||
| const { policy } = policyResult; | ||
| const manifestResult = (0, gate_policy_1.readManifestAtHead)(root, headSha, policy.manifestPath, policy.limits.fileBytes); | ||
| if (manifestResult.manifest.claims.length > policy.limits.claims) { | ||
| throw new Error(`Evidence manifest has ${manifestResult.manifest.claims.length} claims; trusted policy limit is ${policy.limits.claims}.`); | ||
| } | ||
| const allChangedPaths = (0, gate_policy_1.changedPaths)(root, mergeBaseSha, headSha, policy.limits.changedPaths); | ||
| const violations = []; | ||
| const duplicateClaimIds = duplicates(manifestResult.manifest.claims.map((claim) => claim.id)); | ||
| for (const id of duplicateClaimIds) { | ||
| violations.push({ code: 'duplicate-claim-id', claimId: id, message: `Claim ID ${id} is duplicated.` }); | ||
| } | ||
| const requiredPaths = allChangedPaths.filter((path) => path !== policy.manifestPath && | ||
| matchesAny(path, policy.coverage.include) && | ||
| !matchesAny(path, policy.coverage.exclude)); | ||
| if (policy.policyChanges === 'block' && allChangedPaths.includes(policyPath)) { | ||
| violations.push({ | ||
| code: 'policy-change-blocked', | ||
| path: policyPath, | ||
| message: `Trusted gate policy changes must be merged separately: ${policyPath}.`, | ||
| }); | ||
| } | ||
| const claims = []; | ||
| for (const claim of manifestResult.manifest.claims) { | ||
| const claimViolationsBefore = violations.length; | ||
| if (claim.evidence.length > policy.limits.evidencePerClaim) { | ||
| violations.push({ | ||
| code: 'evidence-limit', | ||
| claimId: claim.id, | ||
| message: `Claim ${claim.id} has ${claim.evidence.length} evidence locators; limit is ${policy.limits.evidencePerClaim}.`, | ||
| }); | ||
| } | ||
| for (const path of duplicates(claim.covers)) { | ||
| violations.push({ | ||
| code: 'duplicate-cover', | ||
| claimId: claim.id, | ||
| path, | ||
| message: `Claim ${claim.id} covers ${path} twice.`, | ||
| }); | ||
| } | ||
| const evidence = []; | ||
| for (const locator of claim.evidence.slice(0, policy.limits.evidencePerClaim)) { | ||
| const targetSha = locator.ref === 'base' ? baseSha : headSha; | ||
| let actualContentSha256 = null; | ||
| let citation = null; | ||
| let handle = null; | ||
| let status = 'verified'; | ||
| if (locator.path === policy.manifestPath || (0, policy_1.isAlwaysSensitivePath)(locator.path)) { | ||
| status = 'failed'; | ||
| violations.push({ | ||
| code: locator.path === policy.manifestPath ? 'manifest-self-citation' : 'sensitive-evidence-path', | ||
| claimId: claim.id, | ||
| path: locator.path, | ||
| message: locator.path === policy.manifestPath | ||
| ? `Claim ${claim.id} cannot cite the evidence manifest itself.` | ||
| : `Claim ${claim.id} cannot expose sensitive evidence path ${locator.path}.`, | ||
| }); | ||
| } | ||
| else { | ||
| try { | ||
| const content = (0, gate_policy_1.readGitFile)(root, targetSha, locator.path, policy.limits.fileBytes); | ||
| const lines = content.split(/\r?\n/u); | ||
| if (locator.lineEnd > lines.length || locator.lineEnd - locator.lineStart + 1 > 200) { | ||
| throw new Error(`range ${locator.lineStart}-${locator.lineEnd} is outside the file or exceeds 200 lines`); | ||
| } | ||
| const slice = lines.slice(locator.lineStart - 1, locator.lineEnd).join('\n'); | ||
| actualContentSha256 = (0, gate_policy_1.sha256)(slice); | ||
| if (actualContentSha256 !== locator.contentSha256) { | ||
| status = 'failed'; | ||
| violations.push({ | ||
| code: 'content-hash-mismatch', | ||
| claimId: claim.id, | ||
| path: locator.path, | ||
| message: `Claim ${claim.id} evidence hash does not match ${locator.ref}:${locator.path}:${locator.lineStart}-${locator.lineEnd}.`, | ||
| }); | ||
| } | ||
| else { | ||
| const built = (0, evidence_citation_1.buildCitation)({ | ||
| repository, | ||
| sourcePath: locator.path, | ||
| line: locator.lineStart, | ||
| lineEnd: locator.lineEnd, | ||
| commitSha: targetSha, | ||
| provenance: 'git-head', | ||
| }); | ||
| citation = built.cite; | ||
| handle = built.handle; | ||
| } | ||
| } | ||
| catch (error) { | ||
| status = 'failed'; | ||
| violations.push({ | ||
| code: 'evidence-unavailable', | ||
| claimId: claim.id, | ||
| path: locator.path, | ||
| message: error instanceof Error ? error.message : String(error), | ||
| }); | ||
| } | ||
| } | ||
| evidence.push({ | ||
| ref: locator.ref, | ||
| path: locator.path, | ||
| lineStart: locator.lineStart, | ||
| lineEnd: locator.lineEnd, | ||
| expectedContentSha256: locator.contentSha256, | ||
| actualContentSha256, | ||
| status, | ||
| citation, | ||
| handle, | ||
| }); | ||
| } | ||
| claims.push({ | ||
| id: claim.id, | ||
| statement: claim.statement, | ||
| covers: [...new Set(claim.covers)].sort(), | ||
| evidence, | ||
| status: violations.length === claimViolationsBefore && evidence.every((item) => item.status === 'verified') | ||
| ? 'evidence-verified' | ||
| : 'failed', | ||
| }); | ||
| } | ||
| const coveredPaths = new Set(manifestResult.manifest.claims.flatMap((claim) => claim.covers)); | ||
| const uncovered = requiredPaths.filter((path) => !coveredPaths.has(path)); | ||
| for (const path of uncovered) { | ||
| violations.push({ code: 'uncovered-change', path, message: `Changed path has no material claim: ${path}.` }); | ||
| } | ||
| const reportMaterial = { | ||
| repository, | ||
| baseSha, | ||
| headSha, | ||
| mergeBaseSha, | ||
| policySha256: (0, gate_policy_1.sha256)(policyResult.raw), | ||
| manifestSha256: (0, gate_policy_1.sha256)(manifestResult.raw), | ||
| allChangedPaths, | ||
| requiredPaths, | ||
| claims, | ||
| violations, | ||
| }; | ||
| const status = violations.length === 0 ? 'ok' : 'failed'; | ||
| return { | ||
| kind: 'gitpin-gate-report', | ||
| schemaVersion: 1, | ||
| status, | ||
| reportId: (0, node_crypto_1.createHash)('sha256').update(JSON.stringify(reportMaterial), 'utf8').digest('hex').slice(0, 16), | ||
| repository, | ||
| baseSha, | ||
| headSha, | ||
| mergeBaseSha, | ||
| policy: { path: policyPath, sha256: reportMaterial.policySha256 }, | ||
| manifest: { path: policy.manifestPath, sha256: reportMaterial.manifestSha256 }, | ||
| changedPaths: { all: allChangedPaths, required: requiredPaths, uncovered }, | ||
| claims, | ||
| violations, | ||
| message: status === 'ok' | ||
| ? `Checked ${claims.length} claim manifest entr${claims.length === 1 ? 'y' : 'ies'} and verified their evidence locators for ${requiredPaths.length} changed path(s) at ${headSha}.` | ||
| : `Gate failed with ${violations.length} violation(s). Evidence locators verify committed content, not semantic correctness.`, | ||
| }; | ||
| } | ||
| function duplicates(values) { | ||
| const seen = new Set(); | ||
| const repeated = new Set(); | ||
| for (const value of values) { | ||
| if (seen.has(value)) | ||
| repeated.add(value); | ||
| seen.add(value); | ||
| } | ||
| return [...repeated].sort(); | ||
| } | ||
| function matchesAny(path, patterns) { | ||
| return patterns.some((pattern) => globRegex(pattern).test(path)); | ||
| } | ||
| function globRegex(pattern) { | ||
| let source = '^'; | ||
| for (let index = 0; index < pattern.length; index += 1) { | ||
| const character = pattern[index]; | ||
| if (character === '*' && pattern[index + 1] === '*') { | ||
| if (pattern[index + 2] === '/') { | ||
| source += '(?:.*/)?'; | ||
| index += 2; | ||
| } | ||
| else { | ||
| source += '.*'; | ||
| index += 1; | ||
| } | ||
| } | ||
| else if (character === '*') { | ||
| source += '[^/]*'; | ||
| } | ||
| else if (character === '?') { | ||
| source += '[^/]'; | ||
| } | ||
| else { | ||
| source += character.replace(/[\\^$.[\]{}()+|]/gu, '\\$&'); | ||
| } | ||
| } | ||
| return new RegExp(`${source}$`, 'u'); | ||
| } | ||
| //# sourceMappingURL=gate.js.map |
| {"version":3,"file":"gate.js","sourceRoot":"","sources":["../src/gate.ts"],"names":[],"mappings":"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"} |
| # PR evidence gate | ||
| `gitpin gate` is a read-only required check for agent-authored and human-authored pull requests. It compares the actual merge-base diff with a committed change-evidence manifest, then verifies every evidence locator against an exact base or head commit. | ||
| It verifies **coverage, location, revision, and content hash**. It does not prove semantic correctness, successful runtime behavior, authorship, authorization, or compliance. | ||
| ## Trust boundary | ||
| ```text | ||
| trusted policy = git show <base>:.gitpin/gate.yml | ||
| submitted data = git show <head>:<policy.manifestPath> | ||
| actual change = git diff <merge-base(base,head)>..<head> | ||
| ``` | ||
| The gate never reads policy or evidence from the dirty working tree. A PR therefore cannot weaken its own policy. The manifest intentionally omits `headSha`: a committed file cannot contain the SHA of the commit that contains it. GitPin binds the manifest to `headSha` when it reads the file with `git show`, and emits full-SHA citations in the report. | ||
| ## Bootstrap | ||
| Copy [`templates/gate.yml`](../templates/gate.yml) to `.gitpin/gate.yml` on the default branch. Policy installation must be a separate trusted change; the first protected PR can only run after the base branch contains the policy. | ||
| Every later PR commits `.gitpin/change-evidence.json`. Each material claim: | ||
| - lists the changed paths it covers; | ||
| - contains one or more exact base/head evidence locators; | ||
| - hashes the normalized line slice (`lineStart..lineEnd`, joined with `\n`) with SHA-256. | ||
| Base evidence supports deletions. The manifest itself is excluded from coverage and cannot cite itself. Sensitive paths cannot be exposed as evidence. | ||
| ## GitHub Actions | ||
| ```yaml | ||
| name: Agent delivery assurance | ||
| on: | ||
| pull_request: | ||
| permissions: | ||
| contents: read | ||
| jobs: | ||
| evidence: | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - uses: actions/checkout@v6 | ||
| with: | ||
| fetch-depth: 0 | ||
| ref: ${{ github.event.pull_request.head.sha }} | ||
| - uses: shmindmaster/gitpin@v0.6.0 | ||
| with: | ||
| base-sha: ${{ github.event.pull_request.base.sha }} | ||
| head-sha: ${{ github.event.pull_request.head.sha }} | ||
| ``` | ||
| Use `pull_request`, never `pull_request_target`. Give the job `contents: read`, no secrets, and make it a required workflow with a GitHub ruleset so a PR cannot replace its own enforcement workflow. | ||
| ## CrewScore named controls | ||
| CrewScore complements the evidence gate: it checks whether named written controls are present before execution. Keep it a separate required check and use explicit controls—not its aggregate coverage score. | ||
| ```yaml | ||
| written-controls: | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - uses: actions/checkout@v6 | ||
| with: | ||
| fetch-depth: 0 | ||
| ref: ${{ github.event.pull_request.head.sha }} | ||
| - name: Load trusted CrewScore policy | ||
| shell: bash | ||
| env: | ||
| BASE_SHA: ${{ github.event.pull_request.base.sha }} | ||
| run: git show "$BASE_SHA:.crewscore.yml" > "$RUNNER_TEMP/crewscore.yml" | ||
| - uses: shmindmaster/crewscore@v0.6.8 | ||
| with: | ||
| scan-path: . | ||
| config: ${{ runner.temp }}/crewscore.yml | ||
| threshold: "" | ||
| ``` | ||
| CrewScore observes written text only. It does not prove runtime enforcement, agent obedience, certification, or compliance. Pin its Action version and ruleset, and keep `.crewscore.yml` on the trusted base branch. | ||
| ## Local CLI | ||
| ```bash | ||
| gitpin gate --base <full-40-character-sha> --head <full-40-character-sha> | ||
| ``` | ||
| Exit `0` means all required changed paths have material claims and all locators match committed content. Exit `1` means the gate failed. The deterministic JSON report goes to stdout. |
| { | ||
| "$schema": "https://json-schema.org/draft/2020-12/schema", | ||
| "$id": "https://shmindmaster.github.io/gitpin/schemas/change-evidence.schema.json", | ||
| "title": "GitPin change evidence", | ||
| "type": "object", | ||
| "additionalProperties": false, | ||
| "required": ["schemaVersion", "summary", "claims"], | ||
| "properties": { | ||
| "schemaVersion": { "const": 1 }, | ||
| "summary": { "type": "string", "minLength": 1, "maxLength": 2000 }, | ||
| "claims": { | ||
| "type": "array", | ||
| "maxItems": 100, | ||
| "items": { | ||
| "type": "object", | ||
| "additionalProperties": false, | ||
| "required": ["id", "statement", "covers", "evidence"], | ||
| "properties": { | ||
| "id": { "type": "string", "pattern": "^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$" }, | ||
| "statement": { "type": "string", "minLength": 1, "maxLength": 1000 }, | ||
| "covers": { | ||
| "type": "array", | ||
| "minItems": 1, | ||
| "maxItems": 100, | ||
| "items": { "$ref": "#/$defs/path" } | ||
| }, | ||
| "evidence": { | ||
| "type": "array", | ||
| "minItems": 1, | ||
| "maxItems": 16, | ||
| "items": { | ||
| "type": "object", | ||
| "additionalProperties": false, | ||
| "required": ["ref", "path", "lineStart", "lineEnd", "contentSha256"], | ||
| "properties": { | ||
| "ref": { "enum": ["base", "head"] }, | ||
| "path": { "$ref": "#/$defs/path" }, | ||
| "lineStart": { "type": "integer", "minimum": 1 }, | ||
| "lineEnd": { "type": "integer", "minimum": 1 }, | ||
| "contentSha256": { "type": "string", "pattern": "^[0-9a-f]{64}$" } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| }, | ||
| "$defs": { | ||
| "path": { | ||
| "type": "string", | ||
| "minLength": 1, | ||
| "maxLength": 500, | ||
| "pattern": "^(?!/)(?![A-Za-z]:)(?!.*\\\\)(?!.*(?:^|/)\\.\\.?(?:/|$))(?!.*//)(?!.*\\/$).+$" | ||
| } | ||
| } | ||
| } |
| import { appendFileSync, readFileSync } from 'node:fs'; | ||
| const reportPath = process.argv[2]; | ||
| if (!reportPath) fail('Report path is required.'); | ||
| let report; | ||
| try { | ||
| report = JSON.parse(readFileSync(reportPath, 'utf8')); | ||
| } catch { | ||
| fail('Gate did not produce a valid JSON report.'); | ||
| } | ||
| if ( | ||
| report?.kind !== 'gitpin-gate-report' || | ||
| report.schemaVersion !== 1 || | ||
| !['ok', 'failed'].includes(report.status) || | ||
| !/^[0-9a-f]{16}$/u.test(report.reportId ?? '') || | ||
| !Array.isArray(report.claims) || | ||
| !Array.isArray(report.violations) || | ||
| !Array.isArray(report.changedPaths?.required) | ||
| ) { | ||
| fail('Gate report does not match gitpin-gate-report schema version 1.'); | ||
| } | ||
| if (process.env.GITHUB_OUTPUT) { | ||
| appendFileSync(process.env.GITHUB_OUTPUT, `status=${report.status}\nreport-id=${report.reportId}\n`); | ||
| } | ||
| if (process.env.GITHUB_STEP_SUMMARY) { | ||
| const lines = [ | ||
| '## GitPin Evidence Gate', | ||
| '', | ||
| `**Status:** ${report.status} · **Report:** \`${report.reportId}\``, | ||
| '', | ||
| report.message, | ||
| '', | ||
| `Changed paths requiring claims: ${report.changedPaths.required.length}`, | ||
| `Claims with verified evidence locators: ${report.claims.filter((claim) => claim.status === 'evidence-verified').length}/${report.claims.length}`, | ||
| ]; | ||
| if (report.violations.length) { | ||
| lines.push('', '### Violations', ''); | ||
| for (const violation of report.violations.slice(0, 20)) lines.push(`- ${violation.message}`); | ||
| } | ||
| appendFileSync(process.env.GITHUB_STEP_SUMMARY, `${lines.join('\n')}\n`); | ||
| } | ||
| function fail(message) { | ||
| process.stderr.write(`::error title=GitPin Evidence Gate::${message}\n`); | ||
| process.exit(1); | ||
| } |
| { | ||
| "schemaVersion": 1, | ||
| "summary": "Describe the user-visible or operational outcome of this change.", | ||
| "claims": [ | ||
| { | ||
| "id": "CHANGE-1", | ||
| "statement": "Describe one material claim without asserting more than the evidence shows.", | ||
| "covers": ["src/example.ts"], | ||
| "evidence": [ | ||
| { | ||
| "ref": "head", | ||
| "path": "src/example.ts", | ||
| "lineStart": 1, | ||
| "lineEnd": 10, | ||
| "contentSha256": "0000000000000000000000000000000000000000000000000000000000000000" | ||
| } | ||
| ] | ||
| } | ||
| ] | ||
| } |
| schemaVersion: 1 | ||
| manifestPath: .gitpin/change-evidence.json | ||
| coverage: | ||
| include: | ||
| - "**" | ||
| exclude: | ||
| - "docs/generated/**" | ||
| # A PR cannot weaken its own gate. Merge policy changes separately. | ||
| policyChanges: block | ||
| limits: | ||
| changedPaths: 100 | ||
| claims: 50 | ||
| evidencePerClaim: 8 | ||
| fileBytes: 2097152 |
+13
-1
@@ -7,2 +7,13 @@ # Changelog | ||
| ## [0.6.0] - 2026-07-31 | ||
| ### Added | ||
| - Add a read-only `gitpin gate` command that loads policy from the trusted base commit, binds a submitted manifest to the exact head commit, compares the merge-base diff, and verifies full-SHA line-slice evidence hashes. | ||
| - Add the GitPin Evidence Gate GitHub Action, deterministic JSON reports, base evidence for deletions, bootstrap templates, and documented CrewScore named-control composition. | ||
| ### Changed | ||
| - Reposition GitPin around agent-delivery assurance and required PR evidence rather than repository-context retrieval. | ||
| ## [0.5.3] - 2026-07-31 | ||
@@ -162,3 +173,4 @@ | ||
| [Unreleased]: https://github.com/shmindmaster/gitpin/compare/v0.5.3...HEAD | ||
| [Unreleased]: https://github.com/shmindmaster/gitpin/compare/v0.6.0...HEAD | ||
| [0.6.0]: https://github.com/shmindmaster/gitpin/releases/tag/v0.6.0 | ||
| [0.5.3]: https://github.com/shmindmaster/gitpin/releases/tag/v0.5.3 | ||
@@ -165,0 +177,0 @@ [0.5.2]: https://github.com/shmindmaster/gitpin/releases/tag/v0.5.2 |
+9
-0
@@ -5,2 +5,3 @@ "use strict"; | ||
| const context_brief_1 = require("./context-brief"); | ||
| const cli_gate_1 = require("./cli-gate"); | ||
| const cli_verify_1 = require("./cli-verify"); | ||
@@ -46,2 +47,8 @@ const doctor_1 = require("./doctor"); | ||
| } | ||
| if (command === 'gate') { | ||
| const report = await (0, cli_gate_1.runGateCommand)(options); | ||
| console.log(JSON.stringify(report, null, 2)); | ||
| process.exitCode = (0, cli_verify_1.exitForVerify)(report); | ||
| return; | ||
| } | ||
| if (command === 'help' || command === '--help' || command === '-h') { | ||
@@ -121,2 +128,3 @@ console.log(cliHelp()); | ||
| gitpin prove-set --from-json <items.json> | ||
| gitpin gate --base <full-sha> --head <full-sha> [--root <repo>] [--policy <path>] | ||
@@ -144,2 +152,3 @@ Init options: | ||
| Cite mini-spec: docs/cite-spec.md | ||
| PR evidence gate: docs/pr-evidence-gate.md | ||
| Migration: REPOCONTEXT_* env vars and ~/.repocontext still work as aliases. | ||
@@ -146,0 +155,0 @@ GitPin writes briefs only to stdout. Redirect explicitly for artifacts.`; |
+1
-1
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"} | ||
| {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";;;AAAA,mDAAuF;AACvF,yCAA4C;AAC5C,6CAA4G;AAC5G,qCAA+E;AAC/E,yCAA8C;AAC9C,6CAA6F;AAEtF,KAAK,iBAAiB,IAAc;IACzC,MAAM,CAAC,OAAO,EAAE,GAAG,OAAO,CAAC,GAAG,IAAI,CAAC;IACnC,IAAI,OAAO,KAAK,QAAQ,EAAE,CAAC;QACzB,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,6CAA6C,CAAC,CAAC;QACvF,MAAM,MAAM,GAAG,MAAM,IAAA,wBAAe,GAAE,CAAC;QACvC,OAAO,CAAC,GAAG,CAAC,IAAA,2BAAkB,EAAC,MAAM,CAAC,CAAC,CAAC;QACxC,OAAO,CAAC,QAAQ,GAAG,IAAA,uBAAc,EAAC,MAAM,CAAC,CAAC;QAC1C,OAAO;IACT,CAAC;IACD,IAAI,OAAO,KAAK,OAAO,EAAE,CAAC;QACxB,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,IAAA,+BAAe,EAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QACxF,OAAO;IACT,CAAC;IACD,IAAI,OAAO,KAAK,MAAM,EAAE,CAAC;QACvB,MAAM,MAAM,GAAG,MAAM,IAAA,kCAAqB,EAAC,IAAA,6BAAgB,EAAC,OAAO,CAAC,CAAC,CAAC;QACtE,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC,CAAC;QACtC,OAAO,CAAC,QAAQ,GAAG,IAAA,uBAAc,EAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QACpD,OAAO;IACT,CAAC;IACD,IAAI,OAAO,KAAK,QAAQ,EAAE,CAAC;QACzB,MAAM,MAAM,GAAG,MAAM,IAAA,6BAAgB,EAAC,OAAO,CAAC,CAAC;QAC/C,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QAC7C,OAAO,CAAC,QAAQ,GAAG,IAAA,0BAAa,EAAC,MAAM,CAAC,CAAC;QACzC,OAAO;IACT,CAAC;IACD,IAAI,OAAO,KAAK,WAAW,EAAE,CAAC;QAC5B,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,IAAA,2BAAgB,EAAC,IAAA,iCAAoB,EAAC,OAAO,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QAC5F,OAAO;IACT,CAAC;IACD,IAAI,OAAO,KAAK,cAAc,EAAE,CAAC;QAC/B,MAAM,MAAM,GAAG,MAAM,IAAA,kCAAqB,EAAC,OAAO,CAAC,CAAC;QACpD,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QAC7C,OAAO,CAAC,QAAQ,GAAG,IAAA,0BAAa,EAAC,MAAM,CAAC,CAAC;QACzC,OAAO;IACT,CAAC;IACD,IAAI,OAAO,KAAK,MAAM,EAAE,CAAC;QACvB,MAAM,MAAM,GAAG,MAAM,IAAA,yBAAc,EAAC,OAAO,CAAC,CAAC;QAC7C,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QAC7C,OAAO,CAAC,QAAQ,GAAG,IAAA,0BAAa,EAAC,MAAM,CAAC,CAAC;QACzC,OAAO;IACT,CAAC;IACD,IAAI,OAAO,KAAK,MAAM,IAAI,OAAO,KAAK,QAAQ,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;QACnE,OAAO,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;QACvB,OAAO;IACT,CAAC;IACD,MAAM,IAAI,KAAK,CAAC,oBAAoB,OAAO,6CAA6C,CAAC,CAAC;AAC5F,CAAC;AAED,SAAS,iBAAiB,CAAC,OAAiB;IAC1C,IAAI,QAAQ,GAAkB,WAAW,CAAC;IAC1C,MAAM,YAAY,GAAa,EAAE,CAAC;IAClC,IAAI,gBAAoC,CAAC;IACzC,IAAI,IAAwB,CAAC;IAC7B,IAAI,IAAwB,CAAC;IAE7B,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,OAAO,CAAC,MAAM,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;QACvD,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;QAC9B,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;QACjC,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,UAAU,MAAM,oBAAoB,CAAC,CAAC;QAC5F,QAAQ,MAAM,EAAE,CAAC;YACf,KAAK,YAAY;gBACf,IAAI,CAAC,+BAAe,CAAC,QAAQ,CAAC,KAAsB,CAAC,EAAE,CAAC;oBACtD,MAAM,IAAI,KAAK,CAAC,4BAA4B,+BAAe,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;gBAC7E,CAAC;gBACD,QAAQ,GAAG,KAAsB,CAAC;gBAClC,MAAM;YACR,KAAK,cAAc;gBACjB,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;gBACzB,MAAM;YACR,KAAK,qBAAqB;gBACxB,gBAAgB,GAAG,KAAK,CAAC;gBACzB,MAAM;YACR,KAAK,QAAQ;gBACX,IAAI,GAAG,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;gBACjC,MAAM;YACR,KAAK,QAAQ;gBACX,IAAI,GAAG,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;gBACjC,MAAM;YACR;gBACE,MAAM,IAAI,KAAK,CAAC,yBAAyB,MAAM,gCAAgC,CAAC,CAAC;QACrF,CAAC;QACD,KAAK,IAAI,CAAC,CAAC;IACb,CAAC;IAED,MAAM,YAAY,GAAG,CAAC,gBAAgB,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC;IAC3E,IAAI,YAAY,KAAK,CAAC,IAAI,YAAY,KAAK,CAAC,EAAE,CAAC;QAC7C,MAAM,IAAI,KAAK,CAAC,4EAA4E,CAAC,CAAC;IAChG,CAAC;IACD,IAAI,gBAAgB,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,gBAAgB,CAAC,EAAE,CAAC;QAC5F,MAAM,IAAI,KAAK,CAAC,8DAA8D,CAAC,CAAC;IAClF,CAAC;IACD,OAAO;QACL,QAAQ;QACR,GAAG,CAAC,YAAY,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC,GAAG,IAAI,GAAG,CAAC,YAAY,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAChF,GAAG,CAAC,gBAAgB,IAAI,IAAI,IAAI,IAAI,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,EAAE,UAAU,EAAE,gBAAgB,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAC3G,CAAC;AACJ,CAAC;AAED,SAAS,QAAQ,CAAC,KAAa,EAAE,MAAc;IAC7C,IAAI,CAAC,oBAAoB,CAAC,IAAI,CAAC,KAAK,CAAC;QACnC,MAAM,IAAI,KAAK,CAAC,GAAG,MAAM,qDAAqD,CAAC,CAAC;IAClF,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,OAAO;IACd,OAAO;;;;;;;;;;;;;;;;;wCAiB+B,iCAAoB,CAAC,IAAI,CAAC,IAAI,CAAC;;;;;;;;;;;;;;;;;;;;;;wEAsBC,CAAC;AACzE,CAAC;AAED,SAAS,gBAAgB,CAAC,MAAyD;IACjF,MAAM,KAAK,GAAG,MAAM,CAAC,YAAY,CAAC;IAClC,OAAO;QACL,uBAAuB,MAAM,CAAC,SAAS,CAAC,MAAM,EAAE;QAChD,aAAa,MAAM,CAAC,QAAQ,CAAC,IAAI,KAAK,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,iBAAiB,GAAG;QAChG,IAAA,2BAAkB,EAAC,MAAM,CAAC,SAAS,CAAC;QACpC,EAAE;QACF,mBAAmB,KAAK,CAAC,SAAS,EAAE;QACpC,WAAW,KAAK,CAAC,UAAU,IAAI,KAAK,CAAC,UAAU,IAAI,KAAK,CAAC,IAAI,EAAE;QAC/D,WAAW,KAAK,CAAC,SAAS,EAAE;QAC5B,sCAAsC,KAAK,CAAC,UAAU,WAAW,KAAK,CAAC,UAAU,WAAW,KAAK,CAAC,IAAI,UAAU,KAAK,CAAC,SAAS,EAAE;QACjI,EAAE;QACF,yBAAyB,MAAM,CAAC,MAAM,IAAI;QAC1C,MAAM,CAAC,YAAY;QACnB,EAAE;QACF,+FAA+F;KAChG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC"} |
@@ -197,3 +197,3 @@ "use strict"; | ||
| name: 'GitPin', | ||
| version: '0.5.3', | ||
| version: '0.6.0', | ||
| schema: 'v1', | ||
@@ -200,0 +200,0 @@ mcpServers: [{ name: 'GitPin', type: 'stdio', ...server }], |
+1
-1
@@ -13,3 +13,3 @@ #!/usr/bin/env node | ||
| function createServer() { | ||
| const server = new mcp_js_1.McpServer({ name: 'gitpin', version: '0.5.3' }); | ||
| const server = new mcp_js_1.McpServer({ name: 'gitpin', version: '0.6.0' }); | ||
| (0, pin_tools_1.registerPinTools)(server); | ||
@@ -16,0 +16,0 @@ (0, pin_prompt_1.registerProvePrompt)(server); |
+1
-1
@@ -88,3 +88,3 @@ # MCP client setup (GitPin) | ||
| name: GitPin | ||
| version: 0.5.3 | ||
| version: 0.6.0 | ||
| schema: v1 | ||
@@ -91,0 +91,0 @@ mcpServers: |
+10
-3
| { | ||
| "name": "gitpin", | ||
| "version": "0.5.3", | ||
| "version": "0.6.0", | ||
| "mcpName": "io.github.shmindmaster/gitpin", | ||
| "type": "commonjs", | ||
| "description": "Prove and verify multi-repo agent claims at Git HEAD—index-free evidence packs with path, line, and full SHA.", | ||
| "description": "Verify agent-authored change claims at exact Git commits with an evidence-first PR gate and local MCP.", | ||
| "author": "Sarosh Hussain", | ||
@@ -37,3 +37,7 @@ "license": "MIT", | ||
| "docs", | ||
| "docs/migration-gitpin.md" | ||
| "docs/migration-gitpin.md", | ||
| "action.yml", | ||
| "scripts/render-gate-action-report.mjs", | ||
| "templates/gate.yml", | ||
| "templates/change-evidence.json" | ||
| ], | ||
@@ -102,2 +106,5 @@ "publishConfig": { | ||
| "provenance", | ||
| "pull-request", | ||
| "ci", | ||
| "ai-governance", | ||
| "claude-code", | ||
@@ -104,0 +111,0 @@ "codex", |
+10
-2
@@ -9,5 +9,5 @@ # GitPin | ||
| ### Pin agent answers to Git HEAD. Path. Line. Full SHA. Prove. Verify. | ||
| ### Make agent-authored changes show exact evidence before merge. | ||
| **GitPin is not another “repo context” MCP.** That category is full of vector indexes, dump files, and remote GitHub browsers. GitPin is a **trust product**: index-free, read-only, multi-repo **evidence** agents must prove and humans can re-check with `git show`. | ||
| **GitPin is an agent-delivery assurance gate with a local evidence MCP.** It makes material PR claims cover the actual diff and point to exact committed line slices. The local MCP supplies index-free, read-only, multi-repo evidence that humans and CI can re-check with `git show`. | ||
@@ -32,2 +32,10 @@ ```text | ||
| ## Required PR evidence gate | ||
| ```bash | ||
| gitpin gate --base <full-base-sha> --head <full-head-sha> | ||
| ``` | ||
| The gate reads policy only from the trusted base commit, reads the submitted manifest only from the head commit, compares the merge-base diff, and verifies exact line-slice hashes. It never executes PR code and never labels a locator match as proof of semantic correctness. Use the [GitHub Action and CrewScore named-control setup](docs/pr-evidence-gate.md) as a required check. | ||
| > **Release:** publish a version-matched GitPin release across npm, the MCP Registry, GitHub, and Pages before announcing. Install: `npx -y gitpin@latest`. Node 20+. | ||
@@ -34,0 +42,0 @@ |
+4
-0
@@ -7,2 +7,4 @@ # Roadmap | ||
| - Read-only `gitpin gate` for base-trusted PR policy, changed-path coverage, and exact-commit evidence locators. | ||
| - GitHub Action that emits a deterministic report suitable for a required merge check. | ||
| - Twelve bounded, read-only `pin.*` MCP tools for discovery, evidence, verification, and decision support. | ||
@@ -26,2 +28,4 @@ - Git `HEAD` pinning with path, line, content hash, and full commit provenance. | ||
| - Run the PR gate on real agent-authored changes and measure required-check retention, false blocking, and review decisions changed. | ||
| - Validate CrewScore named controls as a separate written-policy check without using its aggregate coverage score. | ||
| - Validate EvidenceBrief usefulness with technical and cross-functional users using synthetic fixtures. | ||
@@ -28,0 +32,0 @@ - Improve fixtures for workspace repositories, stale documentation, malformed policies, and remote snapshots. |
+2
-2
@@ -10,3 +10,3 @@ { | ||
| }, | ||
| "version": "0.5.3", | ||
| "version": "0.6.0", | ||
| "packages": [ | ||
@@ -16,3 +16,3 @@ { | ||
| "identifier": "gitpin", | ||
| "version": "0.5.3", | ||
| "version": "0.6.0", | ||
| "transport": { | ||
@@ -19,0 +19,0 @@ "type": "stdio" |
@@ -13,2 +13,2 @@ # GitPin client rules (paste into AGENTS.md / Cursor rules / Claude project) | ||
| Install: `npx -y gitpin@0.5.3` and set `GITPIN_REGISTRY`. | ||
| Install: `npx -y gitpin@0.6.0` and set `GITPIN_REGISTRY`. |
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 3 instances
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
AI-detected potential code anomaly
Supply chain riskAI has identified unusual behaviors that may pose a security risk.
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
473751
12.27%141
14.63%5530
14.11%127
6.72%34
25.93%11
10%