





















@@ -7,6 +7,9 @@ import {
77import { isJsonObject, type JsonObject, type JsonValue } from "./protocol.js";
8899const DEFAULT_CODEX_APPROVAL_TIMEOUT_MS = 120_000;
10+const PERMISSION_DESCRIPTION_MAX_LENGTH = 700;
11+const PERMISSION_SAMPLE_LIMIT = 2;
12+const PERMISSION_VALUE_MAX_LENGTH = 48;
10131114export type AppServerApprovalOutcome =
1215| "approved-once"
@@ -208,23 +211,22 @@ function buildApprovalContext(params: {
208211 ? "Codex app-server command approval"
209212 : params.method === "item/permissions/requestApproval"
210213 ? "Codex app-server permission approval"
211- : kind === "plugin"
212- ? "Codex app-server file approval"
213- : "Codex app-server approval";
214+ : kind === "plugin"
215+ ? "Codex app-server file approval"
216+ : "Codex app-server approval";
214217const subject =
215218permissionLines[0] ??
216219(command
217220 ? `Command: ${truncate(command, 180)}`
218221 : reason
219222 ? `Reason: ${truncate(reason, 180)}`
220223 : `Request method: ${params.method}`);
221-const description = joinDescriptionLinesWithinLimit(
222-[
223-subject,
224- ...permissionLines.slice(1),
225-].filter((line): line is string => Boolean(line)),
226-256,
227-);
224+const description =
225+permissionLines.length > 0
226+ ? joinDescriptionLinesWithinLimit(permissionLines, PERMISSION_DESCRIPTION_MAX_LENGTH)
227+ : [subject, params.paramsForRun.sessionKey && `Session: ${params.paramsForRun.sessionKey}`]
228+.filter(Boolean)
229+.join("\n");
228230return {
229231 kind,
230232 title,
@@ -326,6 +328,7 @@ function describeRequestedPermissions(requestParams: JsonObject | undefined): st
326328const permissions = requestedPermissions(requestParams);
327329const lines: string[] = [];
328330const kinds: string[] = [];
331+const risks = new Set<string>();
329332if (isJsonObject(permissions.network)) {
330333kinds.push("network");
331334}
@@ -336,40 +339,45 @@ function describeRequestedPermissions(requestParams: JsonObject | undefined): st
336339lines.push(`Permissions: ${kinds.join(", ")}`);
337340}
338341if (isJsonObject(permissions.network)) {
339-lines.push(
340-summarizePermissionRecord("Network permission requested", permissions.network, [
341-{
342-key: "allowHosts",
343-label: "allowHosts",
344-sanitize: sanitizePermissionHostValue,
345-risksFor: permissionHostRisks,
346-},
347-]),
348-);
342+const networkSummary = summarizePermissionRecord(permissions.network, risks, [
343+{
344+key: "allowHosts",
345+label: "allowHosts",
346+sanitize: sanitizePermissionHostValue,
347+risksFor: permissionHostRisks,
348+},
349+]);
350+if (networkSummary) {
351+lines.push(`Network ${networkSummary}`);
352+}
349353}
350354if (isJsonObject(permissions.fileSystem)) {
351-lines.push(
352-summarizePermissionRecord("File system permission requested", permissions.fileSystem, [
353-{
354-key: "roots",
355-label: "roots",
356-sanitize: sanitizePermissionPathValue,
357-risksFor: permissionPathRisks,
358-},
359-{
360-key: "readPaths",
361-label: "readPaths",
362-sanitize: sanitizePermissionPathValue,
363-risksFor: permissionPathRisks,
364-},
365-{
366-key: "writePaths",
367-label: "writePaths",
368-sanitize: sanitizePermissionPathValue,
369-risksFor: permissionPathRisks,
370-},
371-]),
372-);
355+const fileSystemSummary = summarizePermissionRecord(permissions.fileSystem, risks, [
356+{
357+key: "roots",
358+label: "roots",
359+sanitize: sanitizePermissionPathValue,
360+risksFor: permissionPathRisks,
361+},
362+{
363+key: "readPaths",
364+label: "readPaths",
365+sanitize: sanitizePermissionPathValue,
366+risksFor: permissionPathRisks,
367+},
368+{
369+key: "writePaths",
370+label: "writePaths",
371+sanitize: sanitizePermissionPathValue,
372+risksFor: permissionPathRisks,
373+},
374+]);
375+if (fileSystemSummary) {
376+lines.push(`File system ${fileSystemSummary}`);
377+}
378+}
379+if (risks.size > 0) {
380+lines.push(`High-risk targets: ${[...risks].join(", ")}`);
373381}
374382return lines;
375383}
@@ -382,26 +390,18 @@ type PermissionArrayDescriptor = {
382390};
383391384392function summarizePermissionRecord(
385-label: string,
386393permission: JsonObject,
394+risks: Set<string>,
387395descriptors: readonly PermissionArrayDescriptor[],
388-): string {
396+): string | undefined {
389397const details: string[] = [];
390-const risks = new Set<string>();
391398for (const descriptor of descriptors) {
392399const summary = summarizePermissionArray(permission, descriptor, risks);
393400if (summary) {
394401details.push(summary);
395402}
396403}
397-const suffix: string[] = [];
398-if (details.length > 0) {
399-suffix.push(details.join("; "));
400-}
401-if (risks.size > 0) {
402-suffix.push(`high-risk: ${[...risks].join(", ")}`);
403-}
404-return suffix.length > 0 ? `${label} (${suffix.join("; ")})` : label;
404+return details.length > 0 ? details.join("; ") : undefined;
405405}
406406407407function summarizePermissionArray(
@@ -418,7 +418,10 @@ function summarizePermissionArray(
418418risks.add(risk);
419419}
420420}
421-const sampleValues = values.slice(0, 2).map(descriptor.sanitize).filter(Boolean);
421+const sampleValues = values
422+.slice(0, PERMISSION_SAMPLE_LIMIT)
423+.map(descriptor.sanitize)
424+.filter(Boolean);
422425if (sampleValues.length === 0) {
423426return `${descriptor.label}: ${values.length}`;
424427}
@@ -435,16 +438,31 @@ function readStringArray(record: JsonObject, key: string): string[] {
435438}
436439437440function sanitizePermissionHostValue(value: string): string {
438-return truncate(value.replace(/\s+/g, " ").trim().toLowerCase(), 48);
441+const compact = sanitizePermissionScalar(value).toLowerCase();
442+const withoutScheme = compact.replace(/^[a-z][a-z0-9+.-]*:\/\//, "");
443+const authority = withoutScheme.split(/[/?#]/, 1)[0] ?? withoutScheme;
444+const withoutUserInfo = authority.includes("@")
445+ ? authority.slice(authority.lastIndexOf("@") + 1)
446+ : authority;
447+return truncate(withoutUserInfo, PERMISSION_VALUE_MAX_LENGTH);
439448}
440449441450function sanitizePermissionPathValue(value: string): string {
442-const normalized = value.replace(/\s+/g, " ").trim();
451+const normalized = sanitizePermissionScalar(value);
443452const homeCompacted = normalized
444453.replace(/^\/home\/[^/]+(?=\/|$)/, "~")
445454.replace(/^\/Users\/[^/]+(?=\/|$)/, "~")
446455.replace(/^[A-Za-z]:\\Users\\[^\\]+(?=\\|$)/, "~");
447-return truncate(homeCompacted, 48);
456+return truncate(homeCompacted, PERMISSION_VALUE_MAX_LENGTH);
457+}
458+459+function sanitizePermissionScalar(value: string): string {
460+let sanitized = "";
461+for (let index = 0; index < value.length; index += 1) {
462+const code = value.charCodeAt(index);
463+sanitized += code < 32 || code === 127 ? " " : value[index];
464+}
465+return sanitized.replace(/\s+/g, " ").trim();
448466}
449467450468function permissionHostRisks(value: string): string[] {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。