1. Главная
  2. Docs
  3. Документация по инструментам и технологиям
  4. Cursor
  5. Защита данных при работе с Cursor | Глава 17

Защита данных при работе с Cursor | Глава 17

18 октября 2025
165

ЧАСТЬ V. БЕЗОПАСНОСТЬ И КОНФИДЕНЦИАЛЬНОСТЬ

Глава 17. Защита данных при работе с Cursor

17.1. Политика конфиденциальности Cursor

Понимание обработки данных

Cursor серьезно относится к вопросам конфиденциальности и безопасности пользовательских данных. Важно понимать, какие данные обрабатываются и как они защищены.

Типы обрабатываемых данных:

  1. Код и контекст

    • Фрагменты кода, отправляемые в AI для анализа
    • Структура проекта (без всего содержимого)
    • История изменений в текущей сессии
  2. Метаданные

    • Используемые языки программирования
    • Частота использования функций
    • Производительность системы
  3. Учетные данные

    • Email для аутентификации
    • Информация о подписке
    • Настройки пользователя

Что НЕ отправляется:

  • Полная кодовая база (только релевантные фрагменты)
  • Секреты, токены, пароли (автоматическое обнаружение и фильтрация)
  • Файлы из .gitignore и .cursorignore
  • Бинарные файлы

17.2. Обработка чувствительных данных


You: Как безопасно работать с конфиденциальным кодом в Cursor?

AI:

Best Practices для защиты данных:


# .cursorignore

# Исключить конфиденциальные файлы из индексации Cursor

# Секреты и учетные данные
.env
.env.*
*.pem
*.key
*.p12
*.pfx
secrets/
credentials/

# Конфигурационные файлы с секретами
config/database.yml
config/secrets.yml
aws-credentials.json

# Логи с потенциально чувствительной информацией
*.log
logs/

# Бэкапы и дампы
*.sql
*.dump
backups/

# Проприетарный код (если применимо)
proprietary/
internal/

# Зависимости
node_modules/
vendor/
venv/

Автоматическая защита секретов:


# security/secret_scanner.py

import re
from typing import List, Dict

class SecretScanner:
"""
Сканер для обнаружения потенциальных секретов в коде
"""

PATTERNS = {
'api_key': r'(?i)(api[_-]?key|apikey)[\s]*[:=][\s]*[\'"]?([a-zA-Z0-9_\-]+)[\'"]?',
'aws_key': r'AKIA[0-9A-Z]{16}',
'private_key': r'-----BEGIN (RSA |EC )?PRIVATE KEY-----',
'password': r'(?i)(password|passwd|pwd)[\s]*[:=][\s]*[\'"]([^\'"]+)[\'"]',
'token': r'(?i)(token|auth)[\s]*[:=][\s]*[\'"]?([a-zA-Z0-9_\-\.]+)[\'"]?',
'connection_string': r'(mongodb(\+srv)?|postgres|mysql):\/\/[^\s]+',
}

def scan_file(self, file_path: str) -> List[Dict]:
"""Сканировать файл на наличие секретов"""
findings = []

try:
with open(file_path, 'r', encoding='utf-8') as f:
content = f.read()

for secret_type, pattern in self.PATTERNS.items():
matches = re.finditer(pattern, content)
for match in matches:
findings.append({
'type': secret_type,
'file': file_path,
'line': content[:match.start()].count('\n') + 1,
'match': match.group(0)[:50] # First 50 chars
})
except Exception as e:
print(f"Error scanning {file_path}: {e}")

return findings

def scan_directory(self, directory: str) -> List[Dict]:
"""Сканировать директорию"""
import os
findings = []

for root, dirs, files in os.walk(directory):
# Skip excluded directories
dirs[:] = [d for d in dirs if d not in ['.git', 'node_modules', 'venv']]

for file in files:
if file.endswith(('.py', '.js', '.ts', '.java', '.go', '.rb')):
file_path = os.path.join(root, file)
findings.extend(self.scan_file(file_path))

return findings


# Использование
scanner = SecretScanner()
findings = scanner.scan_directory('.')

if findings:
print("Potential secrets found:")
for finding in findings:
print(f" {finding['type']} in {finding['file']}:{finding['line']}")
else:
print(" No secrets detected")

17.3. Privacy Mode

Включение режима конфиденциальности:

Privacy Mode в Cursor позволяет работать с кодом без отправки его в облако.


// .cursor/settings.json

{
"cursor.privacyMode": true,
"cursor.localModel": true,
"cursor.telemetry": false,
"cursor.analytics": false
}

Возможности в Privacy Mode:

  •  Локальное автодополнение (базовое)
  •  Синтаксическая подсветка
  •  Рефакторинг на основе статического анализа
  •  AI Chat (требует облачных моделей)
  • Сложная генерация кода

Гибридный подход:


# utils/cursor_config.py

"""
Утилита для переключения между режимами
в зависимости от типа проекта
"""

import json
import os

class CursorConfig:
def __init__(self, project_root):
self.project_root = project_root
self.config_path = os.path.join(project_root, '.cursor', 'settings.json')

def enable_privacy_mode(self):
"""Включить режим конфиденциальности"""
config = self._load_config()
config.update({
'cursor.privacyMode': True,
'cursor.telemetry': False
})
self._save_config(config)
print("✓ Privacy mode enabled")

def disable_privacy_mode(self):
"""Выключить режим конфиденциальности"""
config = self._load_config()
config.update({
'cursor.privacyMode': False,
'cursor.telemetry': True
})
self._save_config(config)
print("✓ Privacy mode disabled")

def _load_config(self):
if os.path.exists(self.config_path):
with open(self.config_path, 'r') as f:
return json.load(f)
return {}

def _save_config(self, config):
os.makedirs(os.path.dirname(self.config_path), exist_ok=True)
with open(self.config_path, 'w') as f:
json.dump(config, f, indent=2)


# Использование
config = CursorConfig('.')

# Для работы с конфиденциальным кодом
if is_confidential_project():
config.enable_privacy_mode()
else:
config.disable_privacy_mode()

17.4. Локальное хранение

Конфигурация локального хранилища


You: Настрой Cursor для работы без отправки кода в облако

AI:


// .cursor/local-config.json

{
"storage": {
"mode": "local",
"path": "${HOME}/.cursor/local-storage",
"encryption": true,
"encryptionKey": "${CURSOR_ENCRYPTION_KEY}"
},
"models": {
"useLocalModels": true,
"modelPath": "${HOME}/.cursor/models",
"fallbackToCloud": false
},
"indexing": {
"storeLocally": true,
"indexPath": "${HOME}/.cursor/indexes/${PROJECT_HASH}",
"encryptIndex": true
},
"telemetry": {
"enabled": false,
"crashReports": false,
"usageStatistics": false
}
}

Скрипт для настройки локального окружения:


#!/bin/bash

# setup-local-cursor.sh

set -e

echo "Setting up Cursor for local-only operation..."

# Create directories
CURSOR_HOME="$HOME/.cursor"
mkdir -p "$CURSOR_HOME/local-storage"
mkdir -p "$CURSOR_HOME/models"
mkdir -p "$CURSOR_HOME/indexes"

# Generate encryption key
ENCRYPTION_KEY=$(openssl rand -base64 32)
echo "CURSOR_ENCRYPTION_KEY=$ENCRYPTION_KEY" > "$CURSOR_HOME/.env"

echo "Directories created"
echo "Encryption key generated"

# Set permissions
chmod 700 "$CURSOR_HOME"
chmod 600 "$CURSOR_HOME/.env"

echo "Permissions set"

# Copy local config
cat > "$CURSOR_HOME/local-config.json" <<EOF
{
"storage": {
"mode": "local",
"path": "$CURSOR_HOME/local-storage",
"encryption": true
},
"models": {
"useLocalModels": true,
"modelPath": "$CURSOR_HOME/models"
},
"indexing": {
"storeLocally": true,
"indexPath": "$CURSOR_HOME/indexes"
},
"telemetry": {
"enabled": false
}
}
EOF

echo "✓ Configuration created"

# Download local models (if available)
# This is a placeholder - actual implementation depends on Cursor's API
# download_local_models() {
# echo "Downloading local models..."
# # Model download logic
# }

echo ""
echo "================================"
echo "Local setup completed!"
echo "================================"
echo "Encryption key stored in: $CURSOR_HOME/.env"
echo "Make sure to backup this key securely!"
echo ""
echo "To use local mode, set in Cursor settings:"
echo " Cursor > Privacy Mode: Enabled"
echo " Cursor > Use Local Models: Enabled"

17.5. Соответствие стандартам (GDPR, SOC 2)

GDPR Compliance проверка


You: Создай checklist для GDPR compliance в приложении

AI:


# compliance/gdpr-checklist.yml

---
gdpr_compliance:
lawful_basis:
- item: "Определена законная основа обработки"
status: "required"
implementation:
- "Consent для маркетинговых коммуникаций"
- "Contract для обработки заказов"
- "Legitimate interest для аналитики"

data_minimization:
- item: "Собираются только необходимые данные"
status: "required"
checks:
- "Форма регистрации: email, имя (optional адрес только при заказе)"
- "Не собираем: дату рождения, пол без необходимости"

- item: "Данные удаляются после истечения срока хранения"
status: "required"
implementation: |
- Inactive accounts: 2 года
- Logs: 90 дней
- Analytics: 26 месяцев (Google Analytics стандарт)

user_rights:
- right: "Right to access"
endpoint: "GET /api/users/me/data-export"
implementation: |
Пользователь может запросить полный экспорт своих данных
в машиночитаемом формате (JSON)

- right: "Right to rectification"
endpoint: "PUT /api/users/me"
implementation: "Пользователь может обновить свои данные"

- right: "Right to erasure"
endpoint: "DELETE /api/users/me"
implementation: |
Полное удаление учетной записи и всех связанных данных
с 30-дневным grace period для восстановления

- right: "Right to data portability"
endpoint: "GET /api/users/me/data-export?format=json"
formats: ["JSON", "CSV"]

- right: "Right to object"
endpoint: "POST /api/users/me/consent"
implementation: "Управление согласиями на обработку"

security_measures:
- measure: "Encryption in transit"
implementation:
- "TLS 1.3 для всех соединений"
- "HSTS headers"
- "Certificate pinning в мобильных приложениях"

- measure: "Encryption at rest"
implementation:
- "Database: encryption at rest (AWS RDS)"
- "Backups: encrypted S3 buckets"
- "Passwords: bcrypt with cost factor 12"

- measure: "Access controls"
implementation:
- "Role-based access control (RBAC)"
- "Multi-factor authentication для админов"
- "IP whitelisting для admin panel"

- measure: "Audit logging"
implementation:
- "Все доступы к персональным данным логируются"
- "Логи хранятся в защищенном S3 bucket"
- "Retention: 1 год"

data_processing:
- processor: "AWS (Infrastructure)"
dpa_signed: true
location: "EU (eu-west-1)"

- processor: "Anthropic (AI Processing)"
dpa_signed: true
purpose: "Code analysis and generation"
data_sent: "Code snippets only (no full codebase)"

- processor: "Sentry (Error Monitoring)"
dpa_signed: true
data_sent: "Error logs (sanitized)"

breach_response:
- step: "Detection"
sla: "< 24 hours"

- step: "Assessment"
sla: "< 48 hours"
actions:
- "Определить scope breach"
- "Оценить риски для пользователей"

- step: "Notification"
sla: "< 72 hours"
recipients:
- "Затронутые пользователи"
- "Регулятор (если > 500 пользователей)"

- step: "Remediation"
actions:
- "Устранить уязвимость"
- "Провести security audit"
- "Обновить процедуры"

Реализация GDPR endpoints:


// routes/gdpr.routes.ts

import { Router } from 'express';
import { authenticate } from '../middleware/auth';
import { GDPRController } from '../controllers/gdpr.controller';
import { rateLimit } from 'express-rate-limit';

const router = Router();
const gdprController = new GDPRController();

// Rate limiting для GDPR endpoints
const gdprLimiter = rateLimit({
windowMs: 60 * 60 * 1000, // 1 hour
max: 5, // 5 requests per hour
message: 'Too many GDPR requests, please try again later'
});

/**
* @route GET /api/gdpr/export
* @desc Export all user data (Right to access)
* @access Private
*/
router.get(
'/export',
authenticate,
gdprLimiter,
gdprController.exportUserData
);

/**
* @route DELETE /api/gdpr/delete-account
* @desc Request account deletion (Right to erasure)
* @access Private
*/
router.delete(
'/delete-account',
authenticate,
gdprController.requestAccountDeletion
);

/**
* @route POST /api/gdpr/consent
* @desc Manage consents (Right to object)
* @access Private
*/
router.post(
'/consent',
authenticate,
gdprController.updateConsent
);

/**
* @route GET /api/gdpr/consent
* @desc Get current consents
* @access Private
*/
router.get(
'/consent',
authenticate,
gdprController.getConsents
);

export default router;


// controllers/gdpr.controller.ts

import { Request, Response, NextFunction } from 'express';
import { GDPRService } from '../services/gdpr.service';
import { logger } from '../utils/logger';

export class GDPRController {
private gdprService: GDPRService;

constructor() {
this.gdprService = new GDPRService();
}

async exportUserData(req: Request, res: Response, next: NextFunction) {
try {
const userId = (req as any).user.id;

logger.info(`Data export requested by user: ${userId}`);

// Generate comprehensive data export
const userData = await this.gdprService.exportAllUserData(userId);

// Log the export for audit
await this.gdprService.logDataExport(userId);

// Return as downloadable JSON
res.setHeader('Content-Disposition', `attachment; filename="user-data-${userId}.json"`);
res.setHeader('Content-Type', 'application/json');

res.json({
exportDate: new Date().toISOString(),
userId: userId,
data: userData,
dataProcessors: [
{
name: 'AWS',
purpose: 'Infrastructure hosting',
location: 'EU-WEST-1'
},
{
name: 'Anthropic',
purpose: 'AI code analysis',
dataShared: 'Code snippets only'
}
]
});

} catch (error) {
logger.error('Error exporting user data:', error);
next(error);
}
}

async requestAccountDeletion(req: Request, res: Response, next: NextFunction) {
try {
const userId = (req as any).user.id;
const { reason } = req.body;

logger.info(`Account deletion requested by user: ${userId}`);

// Create deletion request with 30-day grace period
const deletionRequest = await this.gdprService.createDeletionRequest(
userId,
reason
);

// Send confirmation email
await this.gdprService.sendDeletionConfirmationEmail(userId);

res.json({
success: true,
message: 'Account deletion scheduled',
deletionDate: deletionRequest.scheduledDate,
gracePeriod: '30 days',
cancellationDeadline: deletionRequest.cancellationDeadline
});

} catch (error) {
logger.error('Error processing deletion request:', error);
next(error);
}
}

async updateConsent(req: Request, res: Response, next: NextFunction) {
try {
const userId = (req as any).user.id;
const consents = req.body;

// Validate consents
const validConsents = ['marketing', 'analytics', 'personalization'];
for (const key of Object.keys(consents)) {
if (!validConsents.includes(key)) {
return res.status(400).json({
success: false,
error: `Invalid consent type: ${key}`
});
}
}

// Update consents
await this.gdprService.updateUserConsents(userId, consents);

logger.info(`Consents updated for user: ${userId}`, { consents });

res.json({
success: true,
message: 'Consents updated successfully',
consents: consents
});

} catch (error) {
logger.error('Error updating consents:', error);
next(error);
}
}

async getConsents(req: Request, res: Response, next: NextFunction) {
try {
const userId = (req as any).user.id;

const consents = await this.gdprService.getUserConsents(userId);

res.json({
success: true,
data: consents
});

} catch (error) {
next(error);
}
}
}


// services/gdpr.service.ts

import { db } from '../database';
import { EmailService } from './email.service';
import { AuditLogger } from '../utils/audit-logger';

export class GDPRService {
private emailService: EmailService;
private auditLogger: AuditLogger;

constructor() {
this.emailService = new EmailService();
this.auditLogger = new AuditLogger();
}

async exportAllUserData(userId: string) {
/**
* Export comprehensive user data in machine-readable format
*/

// Collect all user-related data
const [
profile,
orders,
addresses,
paymentMethods,
reviews,
wishlist,
activityLog,
consents
] = await Promise.all([
db.users.findById(userId),
db.orders.findByUser(userId),
db.addresses.findByUser(userId),
db.paymentMethods.findByUser(userId),
db.reviews.findByUser(userId),
db.wishlist.findByUser(userId),
db.activityLog.findByUser(userId, { limit: 1000 }),
db.consents.findByUser(userId)
]);

// Sanitize sensitive data
const sanitizedPaymentMethods = paymentMethods.map(pm => ({
...pm,
cardNumber: `****-****-****-${pm.lastFourDigits}`,
cvv: undefined
}));

return {
personalInformation: {
id: profile.id,
email: profile.email,
name: profile.name,
phone: profile.phone,
createdAt: profile.createdAt,
lastLogin: profile.lastLogin
},
orders: orders.map(order => ({
id: order.id,
date: order.createdAt,
total: order.total,
status: order.status,
items: order.items
})),
addresses: addresses,
paymentMethods: sanitizedPaymentMethods,
reviews: reviews,
wishlist: wishlist,
activityLog: activityLog.map(log => ({
action: log.action,
timestamp: log.timestamp,
ipAddress: this.anonymizeIP(log.ipAddress)
})),
consents: consents
};
}

async createDeletionRequest(userId: string, reason?: string) {
const deletionDate = new Date();
deletionDate.setDate(deletionDate.getDate() + 30); // 30-day grace period

const request = await db.deletionRequests.create({
userId,
reason,
requestDate: new Date(),
scheduledDate: deletionDate,
cancellationDeadline: deletionDate,
status: 'pending'
});

// Audit log
await this.auditLogger.log({
action: 'DELETION_REQUEST_CREATED',
userId,
metadata: { requestId: request.id, reason }
});

return request;
}

async executeAccountDeletion(userId: string) {
/**
* Permanently delete user account and all associated data
* This is irreversible
*/

// Start transaction
const session = await db.startSession();
session.startTransaction();

try {
// Log all data before deletion for audit
await this.auditLogger.log({
action: 'ACCOUNT_DELETION_STARTED',
userId,
metadata: {
dataExport: await this.exportAllUserData(userId)
}
});

// Delete from all tables
await Promise.all([
db.orders.deleteByUser(userId, { session }),
db.reviews.deleteByUser(userId, { session }),
db.addresses.deleteByUser(userId, { session }),
db.paymentMethods.deleteByUser(userId, { session }),
db.wishlist.deleteByUser(userId, { session }),
db.activityLog.deleteByUser(userId, { session }),
db.consents.deleteByUser(userId, { session })
]);

// Anonymize data that must be retained for legal reasons
await db.orders.anonymizeUser(userId, { session });

// Finally delete user account
await db.users.delete(userId, { session });

await session.commitTransaction();

// Final audit log
await this.auditLogger.log({
action: 'ACCOUNT_DELETION_COMPLETED',
userId,
metadata: { timestamp: new Date() }
});

logger.info(`Account deleted: ${userId}`);

} catch (error) {
await session.abortTransaction();
logger.error('Error deleting account:', error);
throw error;
} finally {
session.endSession();
}
}

async updateUserConsents(
userId: string,
consents: Record<string, boolean>
) {
const timestamp = new Date();

for (const [consentType, granted] of Object.entries(consents)) {
await db.consents.upsert({
userId,
consentType,
granted,
timestamp
});
}

// If user withdraws marketing consent, unsubscribe
if (consents.marketing === false) {
await this.emailService.unsubscribeUser(userId);
}

// Audit log
await this.auditLogger.log({
action: 'CONSENTS_UPDATED',
userId,
metadata: { consents, timestamp }
});
}

async getUserConsents(userId: string) {
return await db.consents.findByUser(userId);
}

async logDataExport(userId: string) {
await this.auditLogger.log({
action: 'DATA_EXPORTED',
userId,
metadata: {
timestamp: new Date(),
ipAddress: 'tracked separately'
}
});
}

async sendDeletionConfirmationEmail(userId: string) {
const user = await db.users.findById(userId);

await this.emailService.send({
to: user.email,
subject: 'Account Deletion Confirmation',
template: 'account-deletion',
data: {
name: user.name,
deletionDate: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000),
cancellationLink: `${process.env.APP_URL}/account/cancel-deletion`
}
});
}

private anonymizeIP(ip: string): string {
// Anonymize last octet for IPv4
const parts = ip.split('.');
if (parts.length === 4) {
parts[3] = '0';
return parts.join('.');
}
return ip;
}
}