@astrasyncai/verification-gateway 2.0.0 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapter-interface/interface.d.mts +2 -2
- package/dist/adapter-interface/interface.d.ts +2 -2
- package/dist/adapters/express.d.mts +2 -2
- package/dist/adapters/express.d.ts +2 -2
- package/dist/adapters/express.js +118 -5
- package/dist/adapters/express.js.map +1 -1
- package/dist/adapters/express.mjs +118 -5
- package/dist/adapters/express.mjs.map +1 -1
- package/dist/adapters/nextjs.d.mts +2 -2
- package/dist/adapters/nextjs.d.ts +2 -2
- package/dist/adapters/nextjs.js +189 -9
- package/dist/adapters/nextjs.js.map +1 -1
- package/dist/adapters/nextjs.mjs +189 -9
- package/dist/adapters/nextjs.mjs.map +1 -1
- package/dist/adapters/sdk.d.mts +2 -2
- package/dist/adapters/sdk.d.ts +2 -2
- package/dist/adapters/sdk.js +1 -0
- package/dist/adapters/sdk.js.map +1 -1
- package/dist/adapters/sdk.mjs +1 -0
- package/dist/adapters/sdk.mjs.map +1 -1
- package/dist/agent/index.d.mts +2 -2
- package/dist/agent/index.d.ts +2 -2
- package/dist/browser/background.d.mts +2 -0
- package/dist/browser/background.d.ts +2 -0
- package/dist/browser/background.js +4090 -0
- package/dist/browser/background.js.map +1 -0
- package/dist/browser/background.mjs +4088 -0
- package/dist/browser/background.mjs.map +1 -0
- package/dist/browser/browser-adapter.d.mts +10 -6
- package/dist/browser/browser-adapter.d.ts +10 -6
- package/dist/browser/browser-adapter.js +16 -5
- package/dist/browser/browser-adapter.js.map +1 -1
- package/dist/browser/browser-adapter.mjs +14 -4
- package/dist/browser/browser-adapter.mjs.map +1 -1
- package/dist/cli/index.d.mts +2 -2
- package/dist/cli/index.d.ts +2 -2
- package/dist/cli/index.js +1 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/index.mjs +1 -1
- package/dist/cli/index.mjs.map +1 -1
- package/dist/cursor/cursor-adapter.d.mts +3 -4
- package/dist/cursor/cursor-adapter.d.ts +3 -4
- package/dist/cursor/cursor-adapter.js.map +1 -1
- package/dist/cursor/cursor-adapter.mjs.map +1 -1
- package/dist/cursor/extension.d.mts +27 -0
- package/dist/cursor/extension.d.ts +27 -0
- package/dist/cursor/extension.js +4057 -0
- package/dist/cursor/extension.js.map +1 -0
- package/dist/cursor/extension.mjs +4029 -0
- package/dist/cursor/extension.mjs.map +1 -0
- package/dist/{express-DIEyq1Tz.d.ts → express-Bcl-uBUE.d.ts} +1 -1
- package/dist/{express-Cp4eg77F.d.mts → express-CtwDIZyF.d.mts} +1 -1
- package/dist/gateway/gateway.d.mts +2 -2
- package/dist/gateway/gateway.d.ts +2 -2
- package/dist/gateway/gateway.js +17 -17
- package/dist/gateway/gateway.js.map +1 -1
- package/dist/gateway/gateway.mjs +11 -18
- package/dist/gateway/gateway.mjs.map +1 -1
- package/dist/git-trigger/git-hooks.d.mts +2 -2
- package/dist/git-trigger/git-hooks.d.ts +2 -2
- package/dist/git-trigger/git-hooks.js +1 -2
- package/dist/git-trigger/git-hooks.js.map +1 -1
- package/dist/git-trigger/git-hooks.mjs +1 -9
- package/dist/git-trigger/git-hooks.mjs.map +1 -1
- package/dist/{index-CoLebmwv.d.mts → index-B1ThcGZl.d.mts} +1 -1
- package/dist/{index-BhTbGU-o.d.mts → index-BY8yQ8N8.d.mts} +1 -1
- package/dist/{index-Bhfxq9xI.d.ts → index-CtYSYwn3.d.ts} +1 -1
- package/dist/{index-CNkmHmpi.d.ts → index-DnoXfdFd.d.ts} +1 -1
- package/dist/index.d.mts +7 -7
- package/dist/index.d.ts +7 -7
- package/dist/index.js +201 -14
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +201 -14
- package/dist/index.mjs.map +1 -1
- package/dist/local-evaluator/evaluator.d.mts +2 -2
- package/dist/local-evaluator/evaluator.d.ts +2 -2
- package/dist/local-evaluator/evaluator.js.map +1 -1
- package/dist/local-evaluator/evaluator.mjs.map +1 -1
- package/dist/{nextjs-_C_FcJY5.d.mts → nextjs-BQyMCSx_.d.mts} +1 -1
- package/dist/{nextjs-Cag7libc.d.ts → nextjs-CEldnIJ9.d.ts} +1 -1
- package/dist/{sdk-DAJahT3p.d.mts → sdk-BhvuJSrH.d.mts} +1 -1
- package/dist/{sdk-CMPDFUjo.d.ts → sdk-BlyVSC_S.d.ts} +1 -1
- package/dist/transport/index.d.mts +2 -2
- package/dist/transport/index.d.ts +2 -2
- package/dist/{types-Ce2mFJkO.d.ts → types-79qS7aON.d.ts} +2 -2
- package/dist/{types-Bf8pML07.d.mts → types-CxQwJKbd.d.mts} +17 -2
- package/dist/{types-Bf8pML07.d.ts → types-CxQwJKbd.d.ts} +17 -2
- package/dist/{types-BvpGdsv1.d.mts → types-jJnPXStc.d.mts} +2 -2
- package/dist/ui/index.d.mts +1 -1
- package/dist/ui/index.d.ts +1 -1
- package/package.json +3 -2
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { AstraSyncGateway } from '../gateway/gateway.mjs';
|
|
2
|
-
import { V as VerificationDecision, P as PDLSSContext } from '../types-
|
|
3
|
-
import '../types-
|
|
2
|
+
import { V as VerificationDecision, P as PDLSSContext } from '../types-jJnPXStc.mjs';
|
|
3
|
+
import '../types-CxQwJKbd.mjs';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Git Trigger — Enterprise git push / PR verification
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { AstraSyncGateway } from '../gateway/gateway.js';
|
|
2
|
-
import { V as VerificationDecision, P as PDLSSContext } from '../types-
|
|
3
|
-
import '../types-
|
|
2
|
+
import { V as VerificationDecision, P as PDLSSContext } from '../types-79qS7aON.js';
|
|
3
|
+
import '../types-CxQwJKbd.js';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Git Trigger — Enterprise git push / PR verification
|
|
@@ -149,8 +149,7 @@ function uninstallGitHooks(repoRoot, hooks = ["pre-push"]) {
|
|
|
149
149
|
if ((0, import_fs.existsSync)(hookPath)) {
|
|
150
150
|
const content = (0, import_fs.readFileSync)(hookPath, "utf-8");
|
|
151
151
|
if (content.includes(HOOK_MARKER)) {
|
|
152
|
-
|
|
153
|
-
unlinkSync(hookPath);
|
|
152
|
+
(0, import_fs.unlinkSync)(hookPath);
|
|
154
153
|
removed.push(hookPath);
|
|
155
154
|
}
|
|
156
155
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/git-trigger/git-hooks.ts","../../src/adapter-interface/purpose-mapping.ts"],"sourcesContent":["/**\n * Git Trigger — Enterprise git push / PR verification\n *\n * Installs git hooks that evaluate changed files against the PDLSS policy\n * before allowing pushes to remote repositories.\n *\n * Two enforcement modes (admin-configurable):\n * - block: non-zero exit prevents the push\n * - warn: push proceeds but warning is printed\n */\n\nimport { existsSync, mkdirSync, writeFileSync, readFileSync, chmodSync } from 'fs';\nimport { join } from 'path';\nimport { execSync } from 'child_process';\nimport type { AstraSyncGateway } from '../gateway/gateway';\nimport { mapToolToPurpose, extractTarget, extractNetworkDomains } from '../adapter-interface/purpose-mapping';\nimport type { PDLSSContext, VerificationDecision } from '../gateway/types';\n\n// -----------------------------------------------------------------------\n// Configuration\n// -----------------------------------------------------------------------\n\nexport interface GitTriggerConfig {\n /** Block the push or just warn (default: 'warn') */\n enforcement: 'block' | 'warn';\n /** Which git hooks to install (default: ['pre-push']) */\n hooks: ('pre-push' | 'pre-commit')[];\n /** Only enforce on these branches (default: all) */\n protectedBranches?: string[];\n /** Custom message for blocked pushes */\n blockMessage?: string;\n}\n\nconst DEFAULT_CONFIG: GitTriggerConfig = {\n enforcement: 'warn',\n hooks: ['pre-push'],\n};\n\n// -----------------------------------------------------------------------\n// Hook script content\n// -----------------------------------------------------------------------\n\nconst HOOK_MARKER = '# AstraSync Local Guard — managed hook';\n\nfunction makeHookScript(hookType: 'pre-push' | 'pre-commit'): string {\n return `#!/bin/sh\n${HOOK_MARKER}\n# Runs AstraSync verification on changed files before ${hookType === 'pre-push' ? 'push' : 'commit'}.\n# Installed by: astrasync guard install-hooks\n# To remove: delete this file or run astrasync guard uninstall-hooks\n\nnpx astrasync guard check --trigger=${hookType} \"$@\"\nexit $?\n`;\n}\n\n// -----------------------------------------------------------------------\n// Hook installer\n// -----------------------------------------------------------------------\n\n/**\n * Install git hooks into the repository.\n * Returns the paths of installed hooks.\n */\nexport function installGitHooks(\n repoRoot: string,\n config: Partial<GitTriggerConfig> = {},\n): { installed: string[]; skipped: string[] } {\n const merged = { ...DEFAULT_CONFIG, ...config };\n const hooksDir = join(repoRoot, '.git', 'hooks');\n const installed: string[] = [];\n const skipped: string[] = [];\n\n if (!existsSync(join(repoRoot, '.git'))) {\n throw new Error(`Not a git repository: ${repoRoot}`);\n }\n\n if (!existsSync(hooksDir)) {\n mkdirSync(hooksDir, { recursive: true });\n }\n\n for (const hookType of merged.hooks) {\n const hookPath = join(hooksDir, hookType);\n\n // Don't overwrite non-AstraSync hooks\n if (existsSync(hookPath)) {\n const content = readFileSync(hookPath, 'utf-8');\n if (!content.includes(HOOK_MARKER)) {\n skipped.push(hookPath);\n continue;\n }\n }\n\n writeFileSync(hookPath, makeHookScript(hookType), 'utf-8');\n chmodSync(hookPath, 0o755);\n installed.push(hookPath);\n }\n\n return { installed, skipped };\n}\n\n/**\n * Remove AstraSync-managed git hooks.\n */\nexport function uninstallGitHooks(\n repoRoot: string,\n hooks: ('pre-push' | 'pre-commit')[] = ['pre-push'],\n): string[] {\n const hooksDir = join(repoRoot, '.git', 'hooks');\n const removed: string[] = [];\n\n for (const hookType of hooks) {\n const hookPath = join(hooksDir, hookType);\n if (existsSync(hookPath)) {\n const content = readFileSync(hookPath, 'utf-8');\n if (content.includes(HOOK_MARKER)) {\n const { unlinkSync } = require('fs');\n unlinkSync(hookPath);\n removed.push(hookPath);\n }\n }\n }\n\n return removed;\n}\n\n// -----------------------------------------------------------------------\n// Evaluation logic\n// -----------------------------------------------------------------------\n\n/**\n * Get list of changed files for the current trigger context.\n */\nexport function getChangedFiles(trigger: 'pre-push' | 'pre-commit', repoRoot: string): string[] {\n try {\n let output: string;\n if (trigger === 'pre-push') {\n // Files changed between HEAD and remote tracking branch\n output = execSync('git diff --name-only @{push}.. 2>/dev/null || git diff --name-only HEAD~1', {\n cwd: repoRoot,\n encoding: 'utf-8',\n });\n } else {\n // Files staged for commit\n output = execSync('git diff --cached --name-only', {\n cwd: repoRoot,\n encoding: 'utf-8',\n });\n }\n return output.split('\\n').filter(f => f.trim().length > 0);\n } catch {\n return [];\n }\n}\n\n/**\n * Map a file change to a PDLSS context for evaluation.\n */\nexport function fileChangeToPDLSSContext(filePath: string): PDLSSContext {\n const purpose = mapToolToPurpose('file_write');\n const target = extractTarget('file_write', { path: filePath });\n\n return {\n purpose,\n action: 'file_write',\n target,\n dataAccess: ['write'],\n resourceType: 'file',\n };\n}\n\n/**\n * Evaluate changed files against the gateway policy.\n * Returns a summary of allow/deny/review decisions.\n */\nexport async function evaluateChangedFiles(\n gateway: AstraSyncGateway,\n files: string[],\n config: Partial<GitTriggerConfig> = {},\n): Promise<GitCheckResult> {\n const merged = { ...DEFAULT_CONFIG, ...config };\n const results: { file: string; decision: VerificationDecision }[] = [];\n let blocked = 0;\n let warned = 0;\n let allowed = 0;\n\n for (const file of files) {\n const context = fileChangeToPDLSSContext(file);\n const decision = await gateway.evaluate(context);\n\n results.push({ file, decision });\n\n if (decision.recommendation === 'DENY') {\n blocked++;\n } else if (decision.recommendation === 'MANUAL_REVIEW') {\n warned++;\n } else {\n allowed++;\n }\n }\n\n const shouldBlock = merged.enforcement === 'block' && (blocked > 0);\n\n return {\n allowed,\n blocked,\n warned,\n total: files.length,\n shouldBlock,\n enforcement: merged.enforcement,\n results,\n blockMessage: shouldBlock\n ? (merged.blockMessage || `AstraSync blocked push: ${blocked} file(s) denied by policy`)\n : undefined,\n };\n}\n\nexport interface GitCheckResult {\n allowed: number;\n blocked: number;\n warned: number;\n total: number;\n shouldBlock: boolean;\n enforcement: 'block' | 'warn';\n results: { file: string; decision: VerificationDecision }[];\n blockMessage?: string;\n}\n\n/**\n * Check if the current branch is protected.\n */\nexport function isProtectedBranch(config: Partial<GitTriggerConfig> = {}): boolean {\n if (!config.protectedBranches || config.protectedBranches.length === 0) {\n return true; // All branches protected by default\n }\n\n try {\n const branch = execSync('git rev-parse --abbrev-ref HEAD', { encoding: 'utf-8' }).trim();\n return config.protectedBranches.some(pattern => {\n if (pattern === branch) return true;\n if (pattern.endsWith('*') && branch.startsWith(pattern.slice(0, -1))) return true;\n return false;\n });\n } catch {\n return true; // Can't determine branch, assume protected\n }\n}\n","/**\n * Shared purpose mapping utilities for Layer 4 platform adapters.\n *\n * Maps platform-native action names to PDLSS purpose categories.\n * Used by OpenClaw CLI, Cursor, browser, and future adapters.\n */\n\n// -----------------------------------------------------------------------\n// Tool → Purpose mapping\n// -----------------------------------------------------------------------\n\n/** Standard tool name → PDLSS purpose mapping used by all adapters */\nconst TOOL_PURPOSE_MAP: Record<string, string> = {\n // Shell\n shell_exec: 'shell.exec',\n run_command: 'shell.exec',\n execute: 'shell.exec',\n terminal_exec: 'shell.exec',\n run_terminal_command: 'shell.exec',\n\n // File read\n file_read: 'file.read',\n read_file: 'file.read',\n\n // File write\n file_write: 'file.write',\n write_file: 'file.write',\n create_file: 'file.write',\n edit_file: 'file.write',\n\n // File delete\n file_delete: 'file.delete',\n delete_file: 'file.delete',\n\n // Network\n http_request: 'network.request',\n fetch: 'network.request',\n web_request: 'network.request',\n\n // Email\n send_email: 'email.send',\n read_email: 'email.read',\n\n // Calendar\n create_event: 'calendar.create',\n\n // Database\n query_database: 'database.query',\n write_database: 'database.write',\n\n // Payment\n payment_execute: 'payment.execute',\n};\n\n/**\n * Map a tool/action name to a PDLSS purpose category.\n * Returns `tool.<name>` for unmapped tools (denied by default).\n */\nexport function mapToolToPurpose(toolName: string): string {\n return TOOL_PURPOSE_MAP[toolName] || `tool.${toolName}`;\n}\n\n/**\n * Register additional tool → purpose mappings (e.g. from a platform adapter).\n * Does not overwrite existing mappings.\n */\nexport function registerToolMappings(mappings: Record<string, string>): void {\n for (const [tool, purpose] of Object.entries(mappings)) {\n if (!(tool in TOOL_PURPOSE_MAP)) {\n TOOL_PURPOSE_MAP[tool] = purpose;\n }\n }\n}\n\n// -----------------------------------------------------------------------\n// Target extraction\n// -----------------------------------------------------------------------\n\n/**\n * Extract the meaningful target string from tool arguments.\n * Uses the purpose category to determine which argument field is relevant.\n */\nexport function extractTarget(toolName: string, args: Record<string, unknown>): string {\n const purpose = mapToolToPurpose(toolName);\n\n if (purpose.startsWith('shell.')) {\n return String(args.command || args.cmd || args.script || '');\n }\n\n if (purpose.startsWith('file.')) {\n return String(args.path || args.file || args.filename || args.file_path || '');\n }\n\n if (purpose.startsWith('network.')) {\n return String(args.url || args.endpoint || args.uri || '');\n }\n\n if (purpose.startsWith('email.')) {\n return String(args.to || args.recipient || args.address || '');\n }\n\n if (purpose.startsWith('database.')) {\n return String(args.query || args.table || '');\n }\n\n if (purpose.startsWith('payment.')) {\n return String(args.description || args.merchant || args.amount || '');\n }\n\n // Fallback: try common field names\n if (args.command) return String(args.command);\n if (args.path) return String(args.path);\n if (args.url) return String(args.url);\n\n // Default: use first non-empty string argument or tool name\n for (const val of Object.values(args)) {\n if (typeof val === 'string' && val.length > 0) return val;\n }\n return toolName;\n}\n\n// -----------------------------------------------------------------------\n// Network domain extraction\n// -----------------------------------------------------------------------\n\n/**\n * Extract network domains from a URL target.\n * Returns undefined if the target is not a URL.\n */\nexport function extractNetworkDomains(target: string): string[] | undefined {\n try {\n if (target.startsWith('http://') || target.startsWith('https://')) {\n const url = new URL(target);\n return [url.hostname];\n }\n } catch {\n // Not a URL\n }\n return undefined;\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/git-trigger/git-hooks.ts","../../src/adapter-interface/purpose-mapping.ts"],"sourcesContent":["/**\n * Git Trigger — Enterprise git push / PR verification\n *\n * Installs git hooks that evaluate changed files against the PDLSS policy\n * before allowing pushes to remote repositories.\n *\n * Two enforcement modes (admin-configurable):\n * - block: non-zero exit prevents the push\n * - warn: push proceeds but warning is printed\n */\n\nimport { existsSync, mkdirSync, writeFileSync, readFileSync, chmodSync, unlinkSync } from 'fs';\nimport { join } from 'path';\nimport { execSync } from 'child_process';\nimport type { AstraSyncGateway } from '../gateway/gateway';\nimport { mapToolToPurpose, extractTarget } from '../adapter-interface/purpose-mapping';\nimport type { PDLSSContext, VerificationDecision } from '../gateway/types';\n\n// -----------------------------------------------------------------------\n// Configuration\n// -----------------------------------------------------------------------\n\nexport interface GitTriggerConfig {\n /** Block the push or just warn (default: 'warn') */\n enforcement: 'block' | 'warn';\n /** Which git hooks to install (default: ['pre-push']) */\n hooks: ('pre-push' | 'pre-commit')[];\n /** Only enforce on these branches (default: all) */\n protectedBranches?: string[];\n /** Custom message for blocked pushes */\n blockMessage?: string;\n}\n\nconst DEFAULT_CONFIG: GitTriggerConfig = {\n enforcement: 'warn',\n hooks: ['pre-push'],\n};\n\n// -----------------------------------------------------------------------\n// Hook script content\n// -----------------------------------------------------------------------\n\nconst HOOK_MARKER = '# AstraSync Local Guard — managed hook';\n\nfunction makeHookScript(hookType: 'pre-push' | 'pre-commit'): string {\n return `#!/bin/sh\n${HOOK_MARKER}\n# Runs AstraSync verification on changed files before ${hookType === 'pre-push' ? 'push' : 'commit'}.\n# Installed by: astrasync guard install-hooks\n# To remove: delete this file or run astrasync guard uninstall-hooks\n\nnpx astrasync guard check --trigger=${hookType} \"$@\"\nexit $?\n`;\n}\n\n// -----------------------------------------------------------------------\n// Hook installer\n// -----------------------------------------------------------------------\n\n/**\n * Install git hooks into the repository.\n * Returns the paths of installed hooks.\n */\nexport function installGitHooks(\n repoRoot: string,\n config: Partial<GitTriggerConfig> = {},\n): { installed: string[]; skipped: string[] } {\n const merged = { ...DEFAULT_CONFIG, ...config };\n const hooksDir = join(repoRoot, '.git', 'hooks');\n const installed: string[] = [];\n const skipped: string[] = [];\n\n if (!existsSync(join(repoRoot, '.git'))) {\n throw new Error(`Not a git repository: ${repoRoot}`);\n }\n\n if (!existsSync(hooksDir)) {\n mkdirSync(hooksDir, { recursive: true });\n }\n\n for (const hookType of merged.hooks) {\n const hookPath = join(hooksDir, hookType);\n\n // Don't overwrite non-AstraSync hooks\n if (existsSync(hookPath)) {\n const content = readFileSync(hookPath, 'utf-8');\n if (!content.includes(HOOK_MARKER)) {\n skipped.push(hookPath);\n continue;\n }\n }\n\n writeFileSync(hookPath, makeHookScript(hookType), 'utf-8');\n chmodSync(hookPath, 0o755);\n installed.push(hookPath);\n }\n\n return { installed, skipped };\n}\n\n/**\n * Remove AstraSync-managed git hooks.\n */\nexport function uninstallGitHooks(\n repoRoot: string,\n hooks: ('pre-push' | 'pre-commit')[] = ['pre-push'],\n): string[] {\n const hooksDir = join(repoRoot, '.git', 'hooks');\n const removed: string[] = [];\n\n for (const hookType of hooks) {\n const hookPath = join(hooksDir, hookType);\n if (existsSync(hookPath)) {\n const content = readFileSync(hookPath, 'utf-8');\n if (content.includes(HOOK_MARKER)) {\n unlinkSync(hookPath);\n removed.push(hookPath);\n }\n }\n }\n\n return removed;\n}\n\n// -----------------------------------------------------------------------\n// Evaluation logic\n// -----------------------------------------------------------------------\n\n/**\n * Get list of changed files for the current trigger context.\n */\nexport function getChangedFiles(trigger: 'pre-push' | 'pre-commit', repoRoot: string): string[] {\n try {\n let output: string;\n if (trigger === 'pre-push') {\n // Files changed between HEAD and remote tracking branch\n output = execSync('git diff --name-only @{push}.. 2>/dev/null || git diff --name-only HEAD~1', {\n cwd: repoRoot,\n encoding: 'utf-8',\n });\n } else {\n // Files staged for commit\n output = execSync('git diff --cached --name-only', {\n cwd: repoRoot,\n encoding: 'utf-8',\n });\n }\n return output.split('\\n').filter(f => f.trim().length > 0);\n } catch {\n return [];\n }\n}\n\n/**\n * Map a file change to a PDLSS context for evaluation.\n */\nexport function fileChangeToPDLSSContext(filePath: string): PDLSSContext {\n const purpose = mapToolToPurpose('file_write');\n const target = extractTarget('file_write', { path: filePath });\n\n return {\n purpose,\n action: 'file_write',\n target,\n dataAccess: ['write'],\n resourceType: 'file',\n };\n}\n\n/**\n * Evaluate changed files against the gateway policy.\n * Returns a summary of allow/deny/review decisions.\n */\nexport async function evaluateChangedFiles(\n gateway: AstraSyncGateway,\n files: string[],\n config: Partial<GitTriggerConfig> = {},\n): Promise<GitCheckResult> {\n const merged = { ...DEFAULT_CONFIG, ...config };\n const results: { file: string; decision: VerificationDecision }[] = [];\n let blocked = 0;\n let warned = 0;\n let allowed = 0;\n\n for (const file of files) {\n const context = fileChangeToPDLSSContext(file);\n const decision = await gateway.evaluate(context);\n\n results.push({ file, decision });\n\n if (decision.recommendation === 'DENY') {\n blocked++;\n } else if (decision.recommendation === 'MANUAL_REVIEW') {\n warned++;\n } else {\n allowed++;\n }\n }\n\n const shouldBlock = merged.enforcement === 'block' && (blocked > 0);\n\n return {\n allowed,\n blocked,\n warned,\n total: files.length,\n shouldBlock,\n enforcement: merged.enforcement,\n results,\n blockMessage: shouldBlock\n ? (merged.blockMessage || `AstraSync blocked push: ${blocked} file(s) denied by policy`)\n : undefined,\n };\n}\n\nexport interface GitCheckResult {\n allowed: number;\n blocked: number;\n warned: number;\n total: number;\n shouldBlock: boolean;\n enforcement: 'block' | 'warn';\n results: { file: string; decision: VerificationDecision }[];\n blockMessage?: string;\n}\n\n/**\n * Check if the current branch is protected.\n */\nexport function isProtectedBranch(config: Partial<GitTriggerConfig> = {}): boolean {\n if (!config.protectedBranches || config.protectedBranches.length === 0) {\n return true; // All branches protected by default\n }\n\n try {\n const branch = execSync('git rev-parse --abbrev-ref HEAD', { encoding: 'utf-8' }).trim();\n return config.protectedBranches.some(pattern => {\n if (pattern === branch) return true;\n if (pattern.endsWith('*') && branch.startsWith(pattern.slice(0, -1))) return true;\n return false;\n });\n } catch {\n return true; // Can't determine branch, assume protected\n }\n}\n","/**\n * Shared purpose mapping utilities for Layer 4 platform adapters.\n *\n * Maps platform-native action names to PDLSS purpose categories.\n * Used by OpenClaw CLI, Cursor, browser, and future adapters.\n */\n\n// -----------------------------------------------------------------------\n// Tool → Purpose mapping\n// -----------------------------------------------------------------------\n\n/** Standard tool name → PDLSS purpose mapping used by all adapters */\nconst TOOL_PURPOSE_MAP: Record<string, string> = {\n // Shell\n shell_exec: 'shell.exec',\n run_command: 'shell.exec',\n execute: 'shell.exec',\n terminal_exec: 'shell.exec',\n run_terminal_command: 'shell.exec',\n\n // File read\n file_read: 'file.read',\n read_file: 'file.read',\n\n // File write\n file_write: 'file.write',\n write_file: 'file.write',\n create_file: 'file.write',\n edit_file: 'file.write',\n\n // File delete\n file_delete: 'file.delete',\n delete_file: 'file.delete',\n\n // Network\n http_request: 'network.request',\n fetch: 'network.request',\n web_request: 'network.request',\n\n // Email\n send_email: 'email.send',\n read_email: 'email.read',\n\n // Calendar\n create_event: 'calendar.create',\n\n // Database\n query_database: 'database.query',\n write_database: 'database.write',\n\n // Payment\n payment_execute: 'payment.execute',\n};\n\n/**\n * Map a tool/action name to a PDLSS purpose category.\n * Returns `tool.<name>` for unmapped tools (denied by default).\n */\nexport function mapToolToPurpose(toolName: string): string {\n return TOOL_PURPOSE_MAP[toolName] || `tool.${toolName}`;\n}\n\n/**\n * Register additional tool → purpose mappings (e.g. from a platform adapter).\n * Does not overwrite existing mappings.\n */\nexport function registerToolMappings(mappings: Record<string, string>): void {\n for (const [tool, purpose] of Object.entries(mappings)) {\n if (!(tool in TOOL_PURPOSE_MAP)) {\n TOOL_PURPOSE_MAP[tool] = purpose;\n }\n }\n}\n\n// -----------------------------------------------------------------------\n// Target extraction\n// -----------------------------------------------------------------------\n\n/**\n * Extract the meaningful target string from tool arguments.\n * Uses the purpose category to determine which argument field is relevant.\n */\nexport function extractTarget(toolName: string, args: Record<string, unknown>): string {\n const purpose = mapToolToPurpose(toolName);\n\n if (purpose.startsWith('shell.')) {\n return String(args.command || args.cmd || args.script || '');\n }\n\n if (purpose.startsWith('file.')) {\n return String(args.path || args.file || args.filename || args.file_path || '');\n }\n\n if (purpose.startsWith('network.')) {\n return String(args.url || args.endpoint || args.uri || '');\n }\n\n if (purpose.startsWith('email.')) {\n return String(args.to || args.recipient || args.address || '');\n }\n\n if (purpose.startsWith('database.')) {\n return String(args.query || args.table || '');\n }\n\n if (purpose.startsWith('payment.')) {\n return String(args.description || args.merchant || args.amount || '');\n }\n\n // Fallback: try common field names\n if (args.command) return String(args.command);\n if (args.path) return String(args.path);\n if (args.url) return String(args.url);\n\n // Default: use first non-empty string argument or tool name\n for (const val of Object.values(args)) {\n if (typeof val === 'string' && val.length > 0) return val;\n }\n return toolName;\n}\n\n// -----------------------------------------------------------------------\n// Network domain extraction\n// -----------------------------------------------------------------------\n\n/**\n * Extract network domains from a URL target.\n * Returns undefined if the target is not a URL.\n */\nexport function extractNetworkDomains(target: string): string[] | undefined {\n try {\n if (target.startsWith('http://') || target.startsWith('https://')) {\n const url = new URL(target);\n return [url.hostname];\n }\n } catch {\n // Not a URL\n }\n return undefined;\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,12 +1,5 @@
|
|
|
1
|
-
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
2
|
-
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
3
|
-
}) : x)(function(x) {
|
|
4
|
-
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
5
|
-
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
6
|
-
});
|
|
7
|
-
|
|
8
1
|
// src/git-trigger/git-hooks.ts
|
|
9
|
-
import { existsSync, mkdirSync, writeFileSync, readFileSync, chmodSync } from "fs";
|
|
2
|
+
import { existsSync, mkdirSync, writeFileSync, readFileSync, chmodSync, unlinkSync } from "fs";
|
|
10
3
|
import { join } from "path";
|
|
11
4
|
import { execSync } from "child_process";
|
|
12
5
|
|
|
@@ -127,7 +120,6 @@ function uninstallGitHooks(repoRoot, hooks = ["pre-push"]) {
|
|
|
127
120
|
if (existsSync(hookPath)) {
|
|
128
121
|
const content = readFileSync(hookPath, "utf-8");
|
|
129
122
|
if (content.includes(HOOK_MARKER)) {
|
|
130
|
-
const { unlinkSync } = __require("fs");
|
|
131
123
|
unlinkSync(hookPath);
|
|
132
124
|
removed.push(hookPath);
|
|
133
125
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/git-trigger/git-hooks.ts","../../src/adapter-interface/purpose-mapping.ts"],"sourcesContent":["/**\n * Git Trigger — Enterprise git push / PR verification\n *\n * Installs git hooks that evaluate changed files against the PDLSS policy\n * before allowing pushes to remote repositories.\n *\n * Two enforcement modes (admin-configurable):\n * - block: non-zero exit prevents the push\n * - warn: push proceeds but warning is printed\n */\n\nimport { existsSync, mkdirSync, writeFileSync, readFileSync, chmodSync } from 'fs';\nimport { join } from 'path';\nimport { execSync } from 'child_process';\nimport type { AstraSyncGateway } from '../gateway/gateway';\nimport { mapToolToPurpose, extractTarget, extractNetworkDomains } from '../adapter-interface/purpose-mapping';\nimport type { PDLSSContext, VerificationDecision } from '../gateway/types';\n\n// -----------------------------------------------------------------------\n// Configuration\n// -----------------------------------------------------------------------\n\nexport interface GitTriggerConfig {\n /** Block the push or just warn (default: 'warn') */\n enforcement: 'block' | 'warn';\n /** Which git hooks to install (default: ['pre-push']) */\n hooks: ('pre-push' | 'pre-commit')[];\n /** Only enforce on these branches (default: all) */\n protectedBranches?: string[];\n /** Custom message for blocked pushes */\n blockMessage?: string;\n}\n\nconst DEFAULT_CONFIG: GitTriggerConfig = {\n enforcement: 'warn',\n hooks: ['pre-push'],\n};\n\n// -----------------------------------------------------------------------\n// Hook script content\n// -----------------------------------------------------------------------\n\nconst HOOK_MARKER = '# AstraSync Local Guard — managed hook';\n\nfunction makeHookScript(hookType: 'pre-push' | 'pre-commit'): string {\n return `#!/bin/sh\n${HOOK_MARKER}\n# Runs AstraSync verification on changed files before ${hookType === 'pre-push' ? 'push' : 'commit'}.\n# Installed by: astrasync guard install-hooks\n# To remove: delete this file or run astrasync guard uninstall-hooks\n\nnpx astrasync guard check --trigger=${hookType} \"$@\"\nexit $?\n`;\n}\n\n// -----------------------------------------------------------------------\n// Hook installer\n// -----------------------------------------------------------------------\n\n/**\n * Install git hooks into the repository.\n * Returns the paths of installed hooks.\n */\nexport function installGitHooks(\n repoRoot: string,\n config: Partial<GitTriggerConfig> = {},\n): { installed: string[]; skipped: string[] } {\n const merged = { ...DEFAULT_CONFIG, ...config };\n const hooksDir = join(repoRoot, '.git', 'hooks');\n const installed: string[] = [];\n const skipped: string[] = [];\n\n if (!existsSync(join(repoRoot, '.git'))) {\n throw new Error(`Not a git repository: ${repoRoot}`);\n }\n\n if (!existsSync(hooksDir)) {\n mkdirSync(hooksDir, { recursive: true });\n }\n\n for (const hookType of merged.hooks) {\n const hookPath = join(hooksDir, hookType);\n\n // Don't overwrite non-AstraSync hooks\n if (existsSync(hookPath)) {\n const content = readFileSync(hookPath, 'utf-8');\n if (!content.includes(HOOK_MARKER)) {\n skipped.push(hookPath);\n continue;\n }\n }\n\n writeFileSync(hookPath, makeHookScript(hookType), 'utf-8');\n chmodSync(hookPath, 0o755);\n installed.push(hookPath);\n }\n\n return { installed, skipped };\n}\n\n/**\n * Remove AstraSync-managed git hooks.\n */\nexport function uninstallGitHooks(\n repoRoot: string,\n hooks: ('pre-push' | 'pre-commit')[] = ['pre-push'],\n): string[] {\n const hooksDir = join(repoRoot, '.git', 'hooks');\n const removed: string[] = [];\n\n for (const hookType of hooks) {\n const hookPath = join(hooksDir, hookType);\n if (existsSync(hookPath)) {\n const content = readFileSync(hookPath, 'utf-8');\n if (content.includes(HOOK_MARKER)) {\n const { unlinkSync } = require('fs');\n unlinkSync(hookPath);\n removed.push(hookPath);\n }\n }\n }\n\n return removed;\n}\n\n// -----------------------------------------------------------------------\n// Evaluation logic\n// -----------------------------------------------------------------------\n\n/**\n * Get list of changed files for the current trigger context.\n */\nexport function getChangedFiles(trigger: 'pre-push' | 'pre-commit', repoRoot: string): string[] {\n try {\n let output: string;\n if (trigger === 'pre-push') {\n // Files changed between HEAD and remote tracking branch\n output = execSync('git diff --name-only @{push}.. 2>/dev/null || git diff --name-only HEAD~1', {\n cwd: repoRoot,\n encoding: 'utf-8',\n });\n } else {\n // Files staged for commit\n output = execSync('git diff --cached --name-only', {\n cwd: repoRoot,\n encoding: 'utf-8',\n });\n }\n return output.split('\\n').filter(f => f.trim().length > 0);\n } catch {\n return [];\n }\n}\n\n/**\n * Map a file change to a PDLSS context for evaluation.\n */\nexport function fileChangeToPDLSSContext(filePath: string): PDLSSContext {\n const purpose = mapToolToPurpose('file_write');\n const target = extractTarget('file_write', { path: filePath });\n\n return {\n purpose,\n action: 'file_write',\n target,\n dataAccess: ['write'],\n resourceType: 'file',\n };\n}\n\n/**\n * Evaluate changed files against the gateway policy.\n * Returns a summary of allow/deny/review decisions.\n */\nexport async function evaluateChangedFiles(\n gateway: AstraSyncGateway,\n files: string[],\n config: Partial<GitTriggerConfig> = {},\n): Promise<GitCheckResult> {\n const merged = { ...DEFAULT_CONFIG, ...config };\n const results: { file: string; decision: VerificationDecision }[] = [];\n let blocked = 0;\n let warned = 0;\n let allowed = 0;\n\n for (const file of files) {\n const context = fileChangeToPDLSSContext(file);\n const decision = await gateway.evaluate(context);\n\n results.push({ file, decision });\n\n if (decision.recommendation === 'DENY') {\n blocked++;\n } else if (decision.recommendation === 'MANUAL_REVIEW') {\n warned++;\n } else {\n allowed++;\n }\n }\n\n const shouldBlock = merged.enforcement === 'block' && (blocked > 0);\n\n return {\n allowed,\n blocked,\n warned,\n total: files.length,\n shouldBlock,\n enforcement: merged.enforcement,\n results,\n blockMessage: shouldBlock\n ? (merged.blockMessage || `AstraSync blocked push: ${blocked} file(s) denied by policy`)\n : undefined,\n };\n}\n\nexport interface GitCheckResult {\n allowed: number;\n blocked: number;\n warned: number;\n total: number;\n shouldBlock: boolean;\n enforcement: 'block' | 'warn';\n results: { file: string; decision: VerificationDecision }[];\n blockMessage?: string;\n}\n\n/**\n * Check if the current branch is protected.\n */\nexport function isProtectedBranch(config: Partial<GitTriggerConfig> = {}): boolean {\n if (!config.protectedBranches || config.protectedBranches.length === 0) {\n return true; // All branches protected by default\n }\n\n try {\n const branch = execSync('git rev-parse --abbrev-ref HEAD', { encoding: 'utf-8' }).trim();\n return config.protectedBranches.some(pattern => {\n if (pattern === branch) return true;\n if (pattern.endsWith('*') && branch.startsWith(pattern.slice(0, -1))) return true;\n return false;\n });\n } catch {\n return true; // Can't determine branch, assume protected\n }\n}\n","/**\n * Shared purpose mapping utilities for Layer 4 platform adapters.\n *\n * Maps platform-native action names to PDLSS purpose categories.\n * Used by OpenClaw CLI, Cursor, browser, and future adapters.\n */\n\n// -----------------------------------------------------------------------\n// Tool → Purpose mapping\n// -----------------------------------------------------------------------\n\n/** Standard tool name → PDLSS purpose mapping used by all adapters */\nconst TOOL_PURPOSE_MAP: Record<string, string> = {\n // Shell\n shell_exec: 'shell.exec',\n run_command: 'shell.exec',\n execute: 'shell.exec',\n terminal_exec: 'shell.exec',\n run_terminal_command: 'shell.exec',\n\n // File read\n file_read: 'file.read',\n read_file: 'file.read',\n\n // File write\n file_write: 'file.write',\n write_file: 'file.write',\n create_file: 'file.write',\n edit_file: 'file.write',\n\n // File delete\n file_delete: 'file.delete',\n delete_file: 'file.delete',\n\n // Network\n http_request: 'network.request',\n fetch: 'network.request',\n web_request: 'network.request',\n\n // Email\n send_email: 'email.send',\n read_email: 'email.read',\n\n // Calendar\n create_event: 'calendar.create',\n\n // Database\n query_database: 'database.query',\n write_database: 'database.write',\n\n // Payment\n payment_execute: 'payment.execute',\n};\n\n/**\n * Map a tool/action name to a PDLSS purpose category.\n * Returns `tool.<name>` for unmapped tools (denied by default).\n */\nexport function mapToolToPurpose(toolName: string): string {\n return TOOL_PURPOSE_MAP[toolName] || `tool.${toolName}`;\n}\n\n/**\n * Register additional tool → purpose mappings (e.g. from a platform adapter).\n * Does not overwrite existing mappings.\n */\nexport function registerToolMappings(mappings: Record<string, string>): void {\n for (const [tool, purpose] of Object.entries(mappings)) {\n if (!(tool in TOOL_PURPOSE_MAP)) {\n TOOL_PURPOSE_MAP[tool] = purpose;\n }\n }\n}\n\n// -----------------------------------------------------------------------\n// Target extraction\n// -----------------------------------------------------------------------\n\n/**\n * Extract the meaningful target string from tool arguments.\n * Uses the purpose category to determine which argument field is relevant.\n */\nexport function extractTarget(toolName: string, args: Record<string, unknown>): string {\n const purpose = mapToolToPurpose(toolName);\n\n if (purpose.startsWith('shell.')) {\n return String(args.command || args.cmd || args.script || '');\n }\n\n if (purpose.startsWith('file.')) {\n return String(args.path || args.file || args.filename || args.file_path || '');\n }\n\n if (purpose.startsWith('network.')) {\n return String(args.url || args.endpoint || args.uri || '');\n }\n\n if (purpose.startsWith('email.')) {\n return String(args.to || args.recipient || args.address || '');\n }\n\n if (purpose.startsWith('database.')) {\n return String(args.query || args.table || '');\n }\n\n if (purpose.startsWith('payment.')) {\n return String(args.description || args.merchant || args.amount || '');\n }\n\n // Fallback: try common field names\n if (args.command) return String(args.command);\n if (args.path) return String(args.path);\n if (args.url) return String(args.url);\n\n // Default: use first non-empty string argument or tool name\n for (const val of Object.values(args)) {\n if (typeof val === 'string' && val.length > 0) return val;\n }\n return toolName;\n}\n\n// -----------------------------------------------------------------------\n// Network domain extraction\n// -----------------------------------------------------------------------\n\n/**\n * Extract network domains from a URL target.\n * Returns undefined if the target is not a URL.\n */\nexport function extractNetworkDomains(target: string): string[] | undefined {\n try {\n if (target.startsWith('http://') || target.startsWith('https://')) {\n const url = new URL(target);\n return [url.hostname];\n }\n } catch {\n // Not a URL\n }\n return undefined;\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/git-trigger/git-hooks.ts","../../src/adapter-interface/purpose-mapping.ts"],"sourcesContent":["/**\n * Git Trigger — Enterprise git push / PR verification\n *\n * Installs git hooks that evaluate changed files against the PDLSS policy\n * before allowing pushes to remote repositories.\n *\n * Two enforcement modes (admin-configurable):\n * - block: non-zero exit prevents the push\n * - warn: push proceeds but warning is printed\n */\n\nimport { existsSync, mkdirSync, writeFileSync, readFileSync, chmodSync, unlinkSync } from 'fs';\nimport { join } from 'path';\nimport { execSync } from 'child_process';\nimport type { AstraSyncGateway } from '../gateway/gateway';\nimport { mapToolToPurpose, extractTarget } from '../adapter-interface/purpose-mapping';\nimport type { PDLSSContext, VerificationDecision } from '../gateway/types';\n\n// -----------------------------------------------------------------------\n// Configuration\n// -----------------------------------------------------------------------\n\nexport interface GitTriggerConfig {\n /** Block the push or just warn (default: 'warn') */\n enforcement: 'block' | 'warn';\n /** Which git hooks to install (default: ['pre-push']) */\n hooks: ('pre-push' | 'pre-commit')[];\n /** Only enforce on these branches (default: all) */\n protectedBranches?: string[];\n /** Custom message for blocked pushes */\n blockMessage?: string;\n}\n\nconst DEFAULT_CONFIG: GitTriggerConfig = {\n enforcement: 'warn',\n hooks: ['pre-push'],\n};\n\n// -----------------------------------------------------------------------\n// Hook script content\n// -----------------------------------------------------------------------\n\nconst HOOK_MARKER = '# AstraSync Local Guard — managed hook';\n\nfunction makeHookScript(hookType: 'pre-push' | 'pre-commit'): string {\n return `#!/bin/sh\n${HOOK_MARKER}\n# Runs AstraSync verification on changed files before ${hookType === 'pre-push' ? 'push' : 'commit'}.\n# Installed by: astrasync guard install-hooks\n# To remove: delete this file or run astrasync guard uninstall-hooks\n\nnpx astrasync guard check --trigger=${hookType} \"$@\"\nexit $?\n`;\n}\n\n// -----------------------------------------------------------------------\n// Hook installer\n// -----------------------------------------------------------------------\n\n/**\n * Install git hooks into the repository.\n * Returns the paths of installed hooks.\n */\nexport function installGitHooks(\n repoRoot: string,\n config: Partial<GitTriggerConfig> = {},\n): { installed: string[]; skipped: string[] } {\n const merged = { ...DEFAULT_CONFIG, ...config };\n const hooksDir = join(repoRoot, '.git', 'hooks');\n const installed: string[] = [];\n const skipped: string[] = [];\n\n if (!existsSync(join(repoRoot, '.git'))) {\n throw new Error(`Not a git repository: ${repoRoot}`);\n }\n\n if (!existsSync(hooksDir)) {\n mkdirSync(hooksDir, { recursive: true });\n }\n\n for (const hookType of merged.hooks) {\n const hookPath = join(hooksDir, hookType);\n\n // Don't overwrite non-AstraSync hooks\n if (existsSync(hookPath)) {\n const content = readFileSync(hookPath, 'utf-8');\n if (!content.includes(HOOK_MARKER)) {\n skipped.push(hookPath);\n continue;\n }\n }\n\n writeFileSync(hookPath, makeHookScript(hookType), 'utf-8');\n chmodSync(hookPath, 0o755);\n installed.push(hookPath);\n }\n\n return { installed, skipped };\n}\n\n/**\n * Remove AstraSync-managed git hooks.\n */\nexport function uninstallGitHooks(\n repoRoot: string,\n hooks: ('pre-push' | 'pre-commit')[] = ['pre-push'],\n): string[] {\n const hooksDir = join(repoRoot, '.git', 'hooks');\n const removed: string[] = [];\n\n for (const hookType of hooks) {\n const hookPath = join(hooksDir, hookType);\n if (existsSync(hookPath)) {\n const content = readFileSync(hookPath, 'utf-8');\n if (content.includes(HOOK_MARKER)) {\n unlinkSync(hookPath);\n removed.push(hookPath);\n }\n }\n }\n\n return removed;\n}\n\n// -----------------------------------------------------------------------\n// Evaluation logic\n// -----------------------------------------------------------------------\n\n/**\n * Get list of changed files for the current trigger context.\n */\nexport function getChangedFiles(trigger: 'pre-push' | 'pre-commit', repoRoot: string): string[] {\n try {\n let output: string;\n if (trigger === 'pre-push') {\n // Files changed between HEAD and remote tracking branch\n output = execSync('git diff --name-only @{push}.. 2>/dev/null || git diff --name-only HEAD~1', {\n cwd: repoRoot,\n encoding: 'utf-8',\n });\n } else {\n // Files staged for commit\n output = execSync('git diff --cached --name-only', {\n cwd: repoRoot,\n encoding: 'utf-8',\n });\n }\n return output.split('\\n').filter(f => f.trim().length > 0);\n } catch {\n return [];\n }\n}\n\n/**\n * Map a file change to a PDLSS context for evaluation.\n */\nexport function fileChangeToPDLSSContext(filePath: string): PDLSSContext {\n const purpose = mapToolToPurpose('file_write');\n const target = extractTarget('file_write', { path: filePath });\n\n return {\n purpose,\n action: 'file_write',\n target,\n dataAccess: ['write'],\n resourceType: 'file',\n };\n}\n\n/**\n * Evaluate changed files against the gateway policy.\n * Returns a summary of allow/deny/review decisions.\n */\nexport async function evaluateChangedFiles(\n gateway: AstraSyncGateway,\n files: string[],\n config: Partial<GitTriggerConfig> = {},\n): Promise<GitCheckResult> {\n const merged = { ...DEFAULT_CONFIG, ...config };\n const results: { file: string; decision: VerificationDecision }[] = [];\n let blocked = 0;\n let warned = 0;\n let allowed = 0;\n\n for (const file of files) {\n const context = fileChangeToPDLSSContext(file);\n const decision = await gateway.evaluate(context);\n\n results.push({ file, decision });\n\n if (decision.recommendation === 'DENY') {\n blocked++;\n } else if (decision.recommendation === 'MANUAL_REVIEW') {\n warned++;\n } else {\n allowed++;\n }\n }\n\n const shouldBlock = merged.enforcement === 'block' && (blocked > 0);\n\n return {\n allowed,\n blocked,\n warned,\n total: files.length,\n shouldBlock,\n enforcement: merged.enforcement,\n results,\n blockMessage: shouldBlock\n ? (merged.blockMessage || `AstraSync blocked push: ${blocked} file(s) denied by policy`)\n : undefined,\n };\n}\n\nexport interface GitCheckResult {\n allowed: number;\n blocked: number;\n warned: number;\n total: number;\n shouldBlock: boolean;\n enforcement: 'block' | 'warn';\n results: { file: string; decision: VerificationDecision }[];\n blockMessage?: string;\n}\n\n/**\n * Check if the current branch is protected.\n */\nexport function isProtectedBranch(config: Partial<GitTriggerConfig> = {}): boolean {\n if (!config.protectedBranches || config.protectedBranches.length === 0) {\n return true; // All branches protected by default\n }\n\n try {\n const branch = execSync('git rev-parse --abbrev-ref HEAD', { encoding: 'utf-8' }).trim();\n return config.protectedBranches.some(pattern => {\n if (pattern === branch) return true;\n if (pattern.endsWith('*') && branch.startsWith(pattern.slice(0, -1))) return true;\n return false;\n });\n } catch {\n return true; // Can't determine branch, assume protected\n }\n}\n","/**\n * Shared purpose mapping utilities for Layer 4 platform adapters.\n *\n * Maps platform-native action names to PDLSS purpose categories.\n * Used by OpenClaw CLI, Cursor, browser, and future adapters.\n */\n\n// -----------------------------------------------------------------------\n// Tool → Purpose mapping\n// -----------------------------------------------------------------------\n\n/** Standard tool name → PDLSS purpose mapping used by all adapters */\nconst TOOL_PURPOSE_MAP: Record<string, string> = {\n // Shell\n shell_exec: 'shell.exec',\n run_command: 'shell.exec',\n execute: 'shell.exec',\n terminal_exec: 'shell.exec',\n run_terminal_command: 'shell.exec',\n\n // File read\n file_read: 'file.read',\n read_file: 'file.read',\n\n // File write\n file_write: 'file.write',\n write_file: 'file.write',\n create_file: 'file.write',\n edit_file: 'file.write',\n\n // File delete\n file_delete: 'file.delete',\n delete_file: 'file.delete',\n\n // Network\n http_request: 'network.request',\n fetch: 'network.request',\n web_request: 'network.request',\n\n // Email\n send_email: 'email.send',\n read_email: 'email.read',\n\n // Calendar\n create_event: 'calendar.create',\n\n // Database\n query_database: 'database.query',\n write_database: 'database.write',\n\n // Payment\n payment_execute: 'payment.execute',\n};\n\n/**\n * Map a tool/action name to a PDLSS purpose category.\n * Returns `tool.<name>` for unmapped tools (denied by default).\n */\nexport function mapToolToPurpose(toolName: string): string {\n return TOOL_PURPOSE_MAP[toolName] || `tool.${toolName}`;\n}\n\n/**\n * Register additional tool → purpose mappings (e.g. from a platform adapter).\n * Does not overwrite existing mappings.\n */\nexport function registerToolMappings(mappings: Record<string, string>): void {\n for (const [tool, purpose] of Object.entries(mappings)) {\n if (!(tool in TOOL_PURPOSE_MAP)) {\n TOOL_PURPOSE_MAP[tool] = purpose;\n }\n }\n}\n\n// -----------------------------------------------------------------------\n// Target extraction\n// -----------------------------------------------------------------------\n\n/**\n * Extract the meaningful target string from tool arguments.\n * Uses the purpose category to determine which argument field is relevant.\n */\nexport function extractTarget(toolName: string, args: Record<string, unknown>): string {\n const purpose = mapToolToPurpose(toolName);\n\n if (purpose.startsWith('shell.')) {\n return String(args.command || args.cmd || args.script || '');\n }\n\n if (purpose.startsWith('file.')) {\n return String(args.path || args.file || args.filename || args.file_path || '');\n }\n\n if (purpose.startsWith('network.')) {\n return String(args.url || args.endpoint || args.uri || '');\n }\n\n if (purpose.startsWith('email.')) {\n return String(args.to || args.recipient || args.address || '');\n }\n\n if (purpose.startsWith('database.')) {\n return String(args.query || args.table || '');\n }\n\n if (purpose.startsWith('payment.')) {\n return String(args.description || args.merchant || args.amount || '');\n }\n\n // Fallback: try common field names\n if (args.command) return String(args.command);\n if (args.path) return String(args.path);\n if (args.url) return String(args.url);\n\n // Default: use first non-empty string argument or tool name\n for (const val of Object.values(args)) {\n if (typeof val === 'string' && val.length > 0) return val;\n }\n return toolName;\n}\n\n// -----------------------------------------------------------------------\n// Network domain extraction\n// -----------------------------------------------------------------------\n\n/**\n * Extract network domains from a URL target.\n * Returns undefined if the target is not a URL.\n */\nexport function extractNetworkDomains(target: string): string[] | undefined {\n try {\n if (target.startsWith('http://') || target.startsWith('https://')) {\n const url = new URL(target);\n return [url.hostname];\n }\n } catch {\n // Not a URL\n }\n return undefined;\n}\n"],"mappings":"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","names":[]}
|
package/dist/index.d.mts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export {
|
|
3
|
-
export { A as ACCESS_LEVEL_DESCRIPTIONS, a as ACCESS_LEVEL_HIERARCHY, b as AccessCapabilities, D as DEFAULT_TRUST_THRESHOLDS, T as TRUST_LEVEL_RANGES, d as determineAccessLevel, g as getAccessLevelForScore, c as getCapabilities, e as getTrustLevel, h as hasMinimumAccess, s as sdk } from './sdk-
|
|
4
|
-
export { e as express } from './express-
|
|
5
|
-
export { n as nextjs } from './nextjs-
|
|
6
|
-
export { i as transport } from './index-
|
|
7
|
-
export { A as AgentClient, C as ChallengeHandler, i as agent, r as recordDecision } from './index-
|
|
1
|
+
import { b as AgentCredentials, G as GatewayConfig, a as AccessLevel, c as VerificationRequest, V as VerificationResult } from './types-CxQwJKbd.mjs';
|
|
2
|
+
export { A as AstraSyncCredentials, d as CommerceShieldProps, C as CounterpartyType, e as EnhancedVerificationResult, E as ExpressMiddlewareOptions, f as GuidanceInfo, N as NextJsMiddlewareOptions, P as PDLSSInfo, g as ProtocolTransport, R as RouteAccessConfig, h as RuntimeChallengeResult, S as SDKOptions, T as TokenGuidance, i as TrustLevel, j as VerifiedAgent, k as VerifiedDeveloper, l as VerifiedOrganization } from './types-CxQwJKbd.mjs';
|
|
3
|
+
export { A as ACCESS_LEVEL_DESCRIPTIONS, a as ACCESS_LEVEL_HIERARCHY, b as AccessCapabilities, D as DEFAULT_TRUST_THRESHOLDS, T as TRUST_LEVEL_RANGES, d as determineAccessLevel, g as getAccessLevelForScore, c as getCapabilities, e as getTrustLevel, h as hasMinimumAccess, s as sdk } from './sdk-BhvuJSrH.mjs';
|
|
4
|
+
export { e as express } from './express-CtwDIZyF.mjs';
|
|
5
|
+
export { n as nextjs } from './nextjs-BQyMCSx_.mjs';
|
|
6
|
+
export { i as transport } from './index-B1ThcGZl.mjs';
|
|
7
|
+
export { A as AgentClient, C as ChallengeHandler, i as agent, r as recordDecision } from './index-BY8yQ8N8.mjs';
|
|
8
8
|
import 'express';
|
|
9
9
|
import 'next/server';
|
|
10
10
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export {
|
|
3
|
-
export { A as ACCESS_LEVEL_DESCRIPTIONS, a as ACCESS_LEVEL_HIERARCHY, b as AccessCapabilities, D as DEFAULT_TRUST_THRESHOLDS, T as TRUST_LEVEL_RANGES, d as determineAccessLevel, g as getAccessLevelForScore, c as getCapabilities, e as getTrustLevel, h as hasMinimumAccess, s as sdk } from './sdk-
|
|
4
|
-
export { e as express } from './express-
|
|
5
|
-
export { n as nextjs } from './nextjs-
|
|
6
|
-
export { i as transport } from './index-
|
|
7
|
-
export { A as AgentClient, C as ChallengeHandler, i as agent, r as recordDecision } from './index-
|
|
1
|
+
import { b as AgentCredentials, G as GatewayConfig, a as AccessLevel, c as VerificationRequest, V as VerificationResult } from './types-CxQwJKbd.js';
|
|
2
|
+
export { A as AstraSyncCredentials, d as CommerceShieldProps, C as CounterpartyType, e as EnhancedVerificationResult, E as ExpressMiddlewareOptions, f as GuidanceInfo, N as NextJsMiddlewareOptions, P as PDLSSInfo, g as ProtocolTransport, R as RouteAccessConfig, h as RuntimeChallengeResult, S as SDKOptions, T as TokenGuidance, i as TrustLevel, j as VerifiedAgent, k as VerifiedDeveloper, l as VerifiedOrganization } from './types-CxQwJKbd.js';
|
|
3
|
+
export { A as ACCESS_LEVEL_DESCRIPTIONS, a as ACCESS_LEVEL_HIERARCHY, b as AccessCapabilities, D as DEFAULT_TRUST_THRESHOLDS, T as TRUST_LEVEL_RANGES, d as determineAccessLevel, g as getAccessLevelForScore, c as getCapabilities, e as getTrustLevel, h as hasMinimumAccess, s as sdk } from './sdk-BlyVSC_S.js';
|
|
4
|
+
export { e as express } from './express-Bcl-uBUE.js';
|
|
5
|
+
export { n as nextjs } from './nextjs-CEldnIJ9.js';
|
|
6
|
+
export { i as transport } from './index-DnoXfdFd.js';
|
|
7
|
+
export { A as AgentClient, C as ChallengeHandler, i as agent, r as recordDecision } from './index-CtYSYwn3.js';
|
|
8
8
|
import 'express';
|
|
9
9
|
import 'next/server';
|
|
10
10
|
|
package/dist/index.js
CHANGED
|
@@ -280,6 +280,7 @@ async function callVerifyAccessAPI(config, request) {
|
|
|
280
280
|
if (requestData.subAgentDepth !== void 0) body.subAgentDepth = requestData.subAgentDepth;
|
|
281
281
|
if (requestData.enableRuntimeChallenge) body.enableRuntimeChallenge = requestData.enableRuntimeChallenge;
|
|
282
282
|
if (requestData.createSession) body.createSession = requestData.createSession;
|
|
283
|
+
if (requestData.durationRequired) body.durationRequired = requestData.durationRequired;
|
|
283
284
|
if (requestData.counterpartyType) body.counterpartyType = requestData.counterpartyType;
|
|
284
285
|
if (requestData.counterpartyUrl) body.counterpartyUrl = requestData.counterpartyUrl;
|
|
285
286
|
if (requestData.runtimeChallengeOptions) body.runtimeChallengeOptions = requestData.runtimeChallengeOptions;
|
|
@@ -453,6 +454,24 @@ async function recordDecision(config, sessionId, decision, reason) {
|
|
|
453
454
|
}).catch(() => {
|
|
454
455
|
});
|
|
455
456
|
}
|
|
457
|
+
async function reportUnregisteredAttempt(config, data) {
|
|
458
|
+
const apiBaseUrl = config.apiBaseUrl || DEFAULT_CONFIG.apiBaseUrl;
|
|
459
|
+
await fetch(`${apiBaseUrl}/verification-activity/unregistered-attempt`, {
|
|
460
|
+
method: "POST",
|
|
461
|
+
headers: { "Content-Type": "application/json" },
|
|
462
|
+
body: JSON.stringify(data)
|
|
463
|
+
}).catch(() => {
|
|
464
|
+
});
|
|
465
|
+
}
|
|
466
|
+
async function reportCounterpartyPreCheckFailure(config, data) {
|
|
467
|
+
const apiBaseUrl = config.apiBaseUrl || DEFAULT_CONFIG.apiBaseUrl;
|
|
468
|
+
await fetch(`${apiBaseUrl}/verification-activity/counterparty-pre-check-failure`, {
|
|
469
|
+
method: "POST",
|
|
470
|
+
headers: { "Content-Type": "application/json" },
|
|
471
|
+
body: JSON.stringify(data)
|
|
472
|
+
}).catch(() => {
|
|
473
|
+
});
|
|
474
|
+
}
|
|
456
475
|
async function quickVerify(config, credentials) {
|
|
457
476
|
const result = await verify(config, {
|
|
458
477
|
credentials,
|
|
@@ -534,6 +553,54 @@ function extractHttpCredentials(headers) {
|
|
|
534
553
|
return credentials;
|
|
535
554
|
}
|
|
536
555
|
|
|
556
|
+
// src/pdlss-pre-check.ts
|
|
557
|
+
function performCounterpartyPreCheck(routeConfig, astraCreds, purpose) {
|
|
558
|
+
const failures = [];
|
|
559
|
+
if (routeConfig.allowedPurposes && routeConfig.allowedPurposes.length > 0 && purpose) {
|
|
560
|
+
if (!routeConfig.allowedPurposes.includes(purpose)) {
|
|
561
|
+
failures.push({
|
|
562
|
+
field: "purpose",
|
|
563
|
+
requested: purpose,
|
|
564
|
+
limit: routeConfig.allowedPurposes,
|
|
565
|
+
message: `Purpose "${purpose}" is not in the allowed list: [${routeConfig.allowedPurposes.join(", ")}]`
|
|
566
|
+
});
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
if (routeConfig.requiredPurposes && routeConfig.requiredPurposes.length > 0 && purpose) {
|
|
570
|
+
if (!routeConfig.requiredPurposes.includes(purpose)) {
|
|
571
|
+
failures.push({
|
|
572
|
+
field: "purpose",
|
|
573
|
+
requested: purpose,
|
|
574
|
+
limit: routeConfig.requiredPurposes,
|
|
575
|
+
message: `Purpose "${purpose}" is not in the required list: [${routeConfig.requiredPurposes.join(", ")}]`
|
|
576
|
+
});
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
if (routeConfig.maxDuration && astraCreds?.pdlss?.duration?.maxSessionDuration) {
|
|
580
|
+
const requested = astraCreds.pdlss.duration.maxSessionDuration;
|
|
581
|
+
if (requested > routeConfig.maxDuration) {
|
|
582
|
+
failures.push({
|
|
583
|
+
field: "duration",
|
|
584
|
+
requested,
|
|
585
|
+
limit: routeConfig.maxDuration,
|
|
586
|
+
message: `Requested duration ${requested}s exceeds maximum ${routeConfig.maxDuration}s`
|
|
587
|
+
});
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
if (routeConfig.allowedJurisdictions && routeConfig.allowedJurisdictions.length > 0 && astraCreds?.pdlss?.scope?.jurisdiction) {
|
|
591
|
+
const requested = astraCreds.pdlss.scope.jurisdiction;
|
|
592
|
+
if (!routeConfig.allowedJurisdictions.includes(requested)) {
|
|
593
|
+
failures.push({
|
|
594
|
+
field: "jurisdiction",
|
|
595
|
+
requested,
|
|
596
|
+
limit: routeConfig.allowedJurisdictions,
|
|
597
|
+
message: `Jurisdiction "${requested}" is not in the allowed list: [${routeConfig.allowedJurisdictions.join(", ")}]`
|
|
598
|
+
});
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
return failures;
|
|
602
|
+
}
|
|
603
|
+
|
|
537
604
|
// src/adapters/express.ts
|
|
538
605
|
function defaultExtractCredentials(req) {
|
|
539
606
|
return extractCredentials(
|
|
@@ -545,6 +612,12 @@ function extractAstraSyncCredentials(req) {
|
|
|
545
612
|
return extractHttpCredentials(req.headers);
|
|
546
613
|
}
|
|
547
614
|
function defaultExtractPurpose(req) {
|
|
615
|
+
const astraPurpose = req.headers["x-astra-purpose"];
|
|
616
|
+
if (astraPurpose) {
|
|
617
|
+
const value = Array.isArray(astraPurpose) ? astraPurpose[0] : astraPurpose;
|
|
618
|
+
const category = value.split(":")[0];
|
|
619
|
+
return category;
|
|
620
|
+
}
|
|
548
621
|
const purposeHeader = req.headers["x-purpose"] || req.headers["X-Purpose"];
|
|
549
622
|
if (purposeHeader) {
|
|
550
623
|
return Array.isArray(purposeHeader) ? purposeHeader[0] : purposeHeader;
|
|
@@ -554,14 +627,14 @@ function defaultExtractPurpose(req) {
|
|
|
554
627
|
}
|
|
555
628
|
switch (req.method) {
|
|
556
629
|
case "GET":
|
|
557
|
-
return "
|
|
630
|
+
return "read_data";
|
|
558
631
|
case "POST":
|
|
559
|
-
return "
|
|
632
|
+
return "write_data";
|
|
560
633
|
case "PUT":
|
|
561
634
|
case "PATCH":
|
|
562
|
-
return "
|
|
635
|
+
return "write_data";
|
|
563
636
|
case "DELETE":
|
|
564
|
-
return "
|
|
637
|
+
return "delete_data";
|
|
565
638
|
default:
|
|
566
639
|
return "general";
|
|
567
640
|
}
|
|
@@ -598,6 +671,7 @@ function createMiddleware(options) {
|
|
|
598
671
|
skipPaths = [],
|
|
599
672
|
onDenied = defaultOnDenied,
|
|
600
673
|
recordDecisions,
|
|
674
|
+
enableRuntimeChallenge = true,
|
|
601
675
|
...config
|
|
602
676
|
} = options;
|
|
603
677
|
return async (req, res, next) => {
|
|
@@ -615,6 +689,16 @@ function createMiddleware(options) {
|
|
|
615
689
|
}
|
|
616
690
|
const credentials = customExtractCredentials ? customExtractCredentials(req) : defaultExtractCredentials(req);
|
|
617
691
|
if (!hasCredentials(credentials) && routeConfig.minAccessLevel !== "guidance") {
|
|
692
|
+
const counterpartyUrl2 = config.counterpartyUrl || `${req.protocol}://${req.get("host")}`;
|
|
693
|
+
reportUnregisteredAttempt(config, {
|
|
694
|
+
counterpartyUrl: counterpartyUrl2,
|
|
695
|
+
counterpartyType: config.counterpartyType || "api",
|
|
696
|
+
sourceIp: req.ip,
|
|
697
|
+
userAgent: req.headers["user-agent"],
|
|
698
|
+
requestPath: req.path,
|
|
699
|
+
requestMethod: req.method
|
|
700
|
+
}).catch(() => {
|
|
701
|
+
});
|
|
618
702
|
const result2 = {
|
|
619
703
|
verified: false,
|
|
620
704
|
accessLevel: "none",
|
|
@@ -631,7 +715,34 @@ function createMiddleware(options) {
|
|
|
631
715
|
return;
|
|
632
716
|
}
|
|
633
717
|
const purpose = customExtractPurpose ? customExtractPurpose(req) : defaultExtractPurpose(req);
|
|
718
|
+
const astraCreds = extractAstraSyncCredentials(req);
|
|
634
719
|
const counterpartyUrl = config.counterpartyUrl || `${req.protocol}://${req.get("host")}`;
|
|
720
|
+
const preCheckFailures = performCounterpartyPreCheck(routeConfig, astraCreds, purpose);
|
|
721
|
+
if (preCheckFailures.length > 0) {
|
|
722
|
+
const result2 = {
|
|
723
|
+
verified: false,
|
|
724
|
+
accessLevel: "none",
|
|
725
|
+
denialReasons: preCheckFailures.map((f) => f.message),
|
|
726
|
+
guidance: {
|
|
727
|
+
message: "Request exceeds counterparty-defined PDLSS limits.",
|
|
728
|
+
registrationUrl: `${config.apiBaseUrl?.replace("/api", "")}/register`,
|
|
729
|
+
documentationUrl: `${config.apiBaseUrl?.replace("/api", "")}/docs/pdlss`
|
|
730
|
+
},
|
|
731
|
+
verifiedAt: /* @__PURE__ */ new Date()
|
|
732
|
+
};
|
|
733
|
+
req.agentVerification = result2;
|
|
734
|
+
reportCounterpartyPreCheckFailure(config, {
|
|
735
|
+
agentId: astraCreds?.agentId || credentials.astraId || "unknown",
|
|
736
|
+
counterpartyUrl,
|
|
737
|
+
counterpartyType: config.counterpartyType || "api",
|
|
738
|
+
failures: preCheckFailures,
|
|
739
|
+
requestPath: req.path,
|
|
740
|
+
requestMethod: req.method
|
|
741
|
+
}).catch(() => {
|
|
742
|
+
});
|
|
743
|
+
onDenied(result2, req, res);
|
|
744
|
+
return;
|
|
745
|
+
}
|
|
635
746
|
const shouldRecordDecisions = recordDecisions !== false;
|
|
636
747
|
const result = await verify(config, {
|
|
637
748
|
credentials,
|
|
@@ -642,7 +753,9 @@ function createMiddleware(options) {
|
|
|
642
753
|
userAgent: req.headers["user-agent"],
|
|
643
754
|
createSession: shouldRecordDecisions,
|
|
644
755
|
counterpartyUrl,
|
|
645
|
-
counterpartyType: config.counterpartyType || "api"
|
|
756
|
+
counterpartyType: config.counterpartyType || "api",
|
|
757
|
+
enableRuntimeChallenge,
|
|
758
|
+
durationRequired: astraCreds?.pdlss?.duration?.maxSessionDuration
|
|
646
759
|
});
|
|
647
760
|
req.agentVerification = result;
|
|
648
761
|
const sessionId = result.sessionId;
|
|
@@ -737,17 +850,32 @@ function findRouteConfig2(routes, path, method) {
|
|
|
737
850
|
return methodMatches && pathMatches;
|
|
738
851
|
});
|
|
739
852
|
}
|
|
740
|
-
function
|
|
741
|
-
|
|
853
|
+
function extractAstraSyncCredentialsFromNextRequest(request) {
|
|
854
|
+
const headers = {};
|
|
855
|
+
request.headers.forEach((value, key) => {
|
|
856
|
+
headers[key] = value;
|
|
857
|
+
});
|
|
858
|
+
return extractHttpCredentials(headers);
|
|
859
|
+
}
|
|
860
|
+
function extractPurpose(request) {
|
|
861
|
+
const astraPurpose = request.headers.get("x-astra-purpose");
|
|
862
|
+
if (astraPurpose) {
|
|
863
|
+
return astraPurpose.split(":")[0];
|
|
864
|
+
}
|
|
865
|
+
const purposeHeader = request.headers.get("x-purpose");
|
|
866
|
+
if (purposeHeader) {
|
|
867
|
+
return purposeHeader;
|
|
868
|
+
}
|
|
869
|
+
switch (request.method.toUpperCase()) {
|
|
742
870
|
case "GET":
|
|
743
|
-
return "
|
|
871
|
+
return "read_data";
|
|
744
872
|
case "POST":
|
|
745
|
-
return "
|
|
873
|
+
return "write_data";
|
|
746
874
|
case "PUT":
|
|
747
875
|
case "PATCH":
|
|
748
|
-
return "
|
|
876
|
+
return "write_data";
|
|
749
877
|
case "DELETE":
|
|
750
|
-
return "
|
|
878
|
+
return "delete_data";
|
|
751
879
|
default:
|
|
752
880
|
return "general";
|
|
753
881
|
}
|
|
@@ -899,7 +1027,7 @@ function generateCommerceShieldHtml(result, options) {
|
|
|
899
1027
|
`.trim();
|
|
900
1028
|
}
|
|
901
1029
|
function createMiddleware2(options) {
|
|
902
|
-
const { routes = [], skipPaths = [], showCommerceShield = true, ...config } = options;
|
|
1030
|
+
const { routes = [], skipPaths = [], showCommerceShield = true, enableRuntimeChallenge = true, ...config } = options;
|
|
903
1031
|
return async function middleware(request) {
|
|
904
1032
|
const { NextResponse } = await import("next/server");
|
|
905
1033
|
const pathname = request.nextUrl.pathname;
|
|
@@ -916,6 +1044,16 @@ function createMiddleware2(options) {
|
|
|
916
1044
|
}
|
|
917
1045
|
const credentials = extractCredentialsFromNextRequest(request);
|
|
918
1046
|
if (!hasCredentials(credentials) && routeConfig.minAccessLevel !== "guidance") {
|
|
1047
|
+
const counterpartyUrl2 = config.counterpartyUrl || request.nextUrl.origin;
|
|
1048
|
+
reportUnregisteredAttempt(config, {
|
|
1049
|
+
counterpartyUrl: counterpartyUrl2,
|
|
1050
|
+
counterpartyType: config.counterpartyType || "website",
|
|
1051
|
+
sourceIp: request.headers.get("x-forwarded-for") || request.headers.get("x-real-ip") || void 0,
|
|
1052
|
+
userAgent: request.headers.get("user-agent") || void 0,
|
|
1053
|
+
requestPath: pathname,
|
|
1054
|
+
requestMethod: request.method
|
|
1055
|
+
}).catch(() => {
|
|
1056
|
+
});
|
|
919
1057
|
const result2 = {
|
|
920
1058
|
verified: false,
|
|
921
1059
|
accessLevel: "none",
|
|
@@ -953,7 +1091,54 @@ function createMiddleware2(options) {
|
|
|
953
1091
|
return NextResponse.redirect(new URL(registerUrl, request.url));
|
|
954
1092
|
}
|
|
955
1093
|
const counterpartyUrl = config.counterpartyUrl || request.nextUrl.origin;
|
|
956
|
-
const purpose =
|
|
1094
|
+
const purpose = extractPurpose(request);
|
|
1095
|
+
const astraCreds = extractAstraSyncCredentialsFromNextRequest(request);
|
|
1096
|
+
const preCheckFailures = performCounterpartyPreCheck(routeConfig, astraCreds, purpose);
|
|
1097
|
+
if (preCheckFailures.length > 0) {
|
|
1098
|
+
const preCheckResult = {
|
|
1099
|
+
verified: false,
|
|
1100
|
+
accessLevel: "none",
|
|
1101
|
+
denialReasons: preCheckFailures.map((f) => f.message),
|
|
1102
|
+
guidance: {
|
|
1103
|
+
message: "Request exceeds counterparty-defined PDLSS limits.",
|
|
1104
|
+
registrationUrl: `${config.apiBaseUrl?.replace("/api", "")}/register`,
|
|
1105
|
+
documentationUrl: `${config.apiBaseUrl?.replace("/api", "")}/docs/pdlss`
|
|
1106
|
+
},
|
|
1107
|
+
verifiedAt: /* @__PURE__ */ new Date()
|
|
1108
|
+
};
|
|
1109
|
+
reportCounterpartyPreCheckFailure(config, {
|
|
1110
|
+
agentId: astraCreds?.agentId || credentials.astraId || "unknown",
|
|
1111
|
+
counterpartyUrl,
|
|
1112
|
+
counterpartyType: config.counterpartyType || "website",
|
|
1113
|
+
failures: preCheckFailures,
|
|
1114
|
+
requestPath: pathname,
|
|
1115
|
+
requestMethod: request.method
|
|
1116
|
+
}).catch(() => {
|
|
1117
|
+
});
|
|
1118
|
+
if (pathname.startsWith("/api/")) {
|
|
1119
|
+
return NextResponse.json(
|
|
1120
|
+
{
|
|
1121
|
+
success: false,
|
|
1122
|
+
error: {
|
|
1123
|
+
code: "PDLSS_PRE_CHECK_FAILED",
|
|
1124
|
+
message: preCheckResult.denialReasons?.[0] || "PDLSS pre-check failed",
|
|
1125
|
+
guidance: preCheckResult.guidance
|
|
1126
|
+
}
|
|
1127
|
+
},
|
|
1128
|
+
{ status: 403 }
|
|
1129
|
+
);
|
|
1130
|
+
}
|
|
1131
|
+
if (showCommerceShield) {
|
|
1132
|
+
return new NextResponse(generateCommerceShieldHtml(preCheckResult, options), {
|
|
1133
|
+
status: 200,
|
|
1134
|
+
headers: {
|
|
1135
|
+
"Content-Type": "text/html",
|
|
1136
|
+
"X-AstraSync-Verification": "commerce-shield"
|
|
1137
|
+
}
|
|
1138
|
+
});
|
|
1139
|
+
}
|
|
1140
|
+
return NextResponse.redirect(new URL("/unauthorized", request.url));
|
|
1141
|
+
}
|
|
957
1142
|
const result = await verify(config, {
|
|
958
1143
|
credentials,
|
|
959
1144
|
purpose,
|
|
@@ -962,7 +1147,9 @@ function createMiddleware2(options) {
|
|
|
962
1147
|
clientIp: request.headers.get("x-forwarded-for")?.split(",")[0]?.trim() || void 0,
|
|
963
1148
|
userAgent: request.headers.get("user-agent") || void 0,
|
|
964
1149
|
counterpartyUrl,
|
|
965
|
-
counterpartyType: config.counterpartyType || "website"
|
|
1150
|
+
counterpartyType: config.counterpartyType || "website",
|
|
1151
|
+
enableRuntimeChallenge,
|
|
1152
|
+
durationRequired: astraCreds?.pdlss?.duration?.maxSessionDuration
|
|
966
1153
|
});
|
|
967
1154
|
if (!hasMinimumAccess(result.accessLevel, routeConfig.minAccessLevel)) {
|
|
968
1155
|
if (pathname.startsWith("/api/")) {
|