File size: 4,905 Bytes
84aa3bf
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
/**
 * @license
 * Copyright 2025 Google LLC
 * SPDX-License-Identifier: Apache-2.0
 */

import {
  type PolicyEngineConfig,
  type ApprovalMode,
  type PolicyEngine,
  type MessageBus,
  type PolicySettings,
  createPolicyEngineConfig as createCorePolicyEngineConfig,
  createPolicyUpdater as createCorePolicyUpdater,
  PolicyIntegrityManager,
  IntegrityStatus,
  Storage,
  type PolicyUpdateConfirmationRequest,
  writeToStderr,
  debugLogger,
} from '@google/gemini-cli-core';
import { type Settings } from './settings.js';

/**
 * Temporary flag to automatically accept workspace policies to reduce friction.
 * Exported as 'let' to allow monkey patching in tests via the setter.
 */
export let autoAcceptWorkspacePolicies = true;

/**
 * Sets the autoAcceptWorkspacePolicies flag.
 * Used primarily for testing purposes.
 */
export function setAutoAcceptWorkspacePolicies(value: boolean) {
  autoAcceptWorkspacePolicies = value;
}

/**
 * Temporary flag to disable workspace level policies altogether.
 * Exported as 'let' to allow monkey patching in tests via the setter.
 */
export let disableWorkspacePolicies = true;

/**
 * Sets the disableWorkspacePolicies flag.
 * Used primarily for testing purposes.
 */
export function setDisableWorkspacePolicies(value: boolean) {
  disableWorkspacePolicies = value;
}

export async function createPolicyEngineConfig(
  settings: Settings,
  approvalMode: ApprovalMode,
  workspacePoliciesDir?: string,
  interactive: boolean = true,
): Promise<PolicyEngineConfig> {
  // Explicitly construct PolicySettings from Settings to ensure type safety
  // and avoid accidental leakage of other settings properties.
  const policySettings: PolicySettings = {
    mcp: settings.mcp,
    tools: settings.tools,
    mcpServers: settings.mcpServers,
    policyPaths: settings.policyPaths,
    adminPolicyPaths: settings.adminPolicyPaths,
    workspacePoliciesDir,
    disableAlwaysAllow:
      settings.security?.disableAlwaysAllow ||
      settings.admin?.secureModeEnabled,
  };

  return createCorePolicyEngineConfig(
    policySettings,
    approvalMode,
    undefined,
    interactive,
  );
}

export function createPolicyUpdater(
  policyEngine: PolicyEngine,
  messageBus: MessageBus,
  storage: Storage,
) {
  return createCorePolicyUpdater(policyEngine, messageBus, storage);
}

export interface WorkspacePolicyState {
  workspacePoliciesDir?: string;
  policyUpdateConfirmationRequest?: PolicyUpdateConfirmationRequest;
}

/**
 * Resolves the workspace policy state by checking folder trust and policy integrity.
 */
export async function resolveWorkspacePolicyState(options: {
  cwd: string;
  trustedFolder: boolean;
  interactive: boolean;
}): Promise<WorkspacePolicyState> {
  const { cwd, trustedFolder, interactive } = options;

  let workspacePoliciesDir: string | undefined;
  let policyUpdateConfirmationRequest:
    | PolicyUpdateConfirmationRequest
    | undefined;

  if (trustedFolder && !disableWorkspacePolicies) {
    const storage = new Storage(cwd);

    // If we are in the home directory (or rather, our target Gemini dir is the global one),
    // don't treat it as a workspace to avoid loading global policies twice.
    if (storage.isWorkspaceHomeDir()) {
      return { workspacePoliciesDir: undefined };
    }

    const potentialWorkspacePoliciesDir = storage.getWorkspacePoliciesDir();
    const integrityManager = new PolicyIntegrityManager();
    const integrityResult = await integrityManager.checkIntegrity(
      'workspace',
      cwd,
      potentialWorkspacePoliciesDir,
    );

    if (integrityResult.status === IntegrityStatus.MATCH) {
      workspacePoliciesDir = potentialWorkspacePoliciesDir;
    } else if (
      integrityResult.status === IntegrityStatus.NEW &&
      integrityResult.fileCount === 0
    ) {
      // No workspace policies found
      workspacePoliciesDir = undefined;
    } else if (interactive && !autoAcceptWorkspacePolicies) {
      // Policies changed or are new, and we are in interactive mode and auto-accept is disabled
      policyUpdateConfirmationRequest = {
        scope: 'workspace',
        identifier: cwd,
        policyDir: potentialWorkspacePoliciesDir,
        newHash: integrityResult.hash,
      };
    } else {
      // Non-interactive mode or auto-accept is enabled: automatically accept/load
      await integrityManager.acceptIntegrity(
        'workspace',
        cwd,
        integrityResult.hash,
      );
      workspacePoliciesDir = potentialWorkspacePoliciesDir;

      if (!interactive) {
        writeToStderr(
          'WARNING: Workspace policies changed or are new. Automatically accepting and loading them.\n',
        );
      } else {
        debugLogger.warn(
          'Workspace policies changed or are new. Automatically accepting and loading them.',
        );
      }
    }
  }

  return { workspacePoliciesDir, policyUpdateConfirmationRequest };
}