Yuki Takei 9 месяцев назад
Родитель
Сommit
047d383578

+ 2 - 0
apps/app/src/features/opentelemetry/docs/improvement-task.md

@@ -25,10 +25,12 @@
 - [x] **HTTP匿名化**
   - [x] クエリパラメータマスキング(`q`, `query`, `search`)
   - [x] HTTP Instrumentation統合
+  - [x] 未使用コードの削除・リファクタリング
   - [ ] 認証ヘッダー除外(`authorization`, `cookie`)
   - [ ] IPアドレス部分マスキング
 - [x] **設定機能**
   - [x] 匿名化有効/無効制御(環境変数)
+  - [x] 設定の定数化
   - [ ] カスタム除外パターン設定
 
 ### Phase 3: 統合・テスト

+ 8 - 118
apps/app/src/features/opentelemetry/server/anonymization/anonymize-http-requests.ts

@@ -1,135 +1,25 @@
 import { diag } from '@opentelemetry/api';
-import type { Span } from '@opentelemetry/api';
 import type { InstrumentationConfigMap } from '@opentelemetry/auto-instrumentations-node';
 
 const logger = diag.createComponentLogger({ namespace: 'growi:anonymization:query' });
 
-export interface AnonymizeHttpRequestsConfig {
-  sensitiveParams: string[];
-  maskPattern: string;
-}
-
-function escapeRegExp(string: string): string {
-  return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
-}
-
-function anonymizeQueryString(query: string, config: AnonymizeHttpRequestsConfig): string {
-  let result = query;
-
-  config.sensitiveParams.forEach((param) => {
-    // Match with (?:^|[?&])param=([^&]*) pattern
-    const pattern = new RegExp(`((?:^|[?&])${escapeRegExp(param)}=)([^&]*)`, 'gi');
-    result = result.replace(pattern, `$1${config.maskPattern}`);
-  });
-
-  return result;
-}
-
-function anonymizeUrl(url: string, config: AnonymizeHttpRequestsConfig): string {
-  try {
-    const urlObj = new URL(url);
-
-    // Mask sensitive parameters
-    let modified = false;
-    config.sensitiveParams.forEach((param) => {
-      if (urlObj.searchParams.has(param)) {
-        urlObj.searchParams.set(param, config.maskPattern);
-        modified = true;
-      }
-    });
-
-    return modified ? urlObj.toString() : url;
-  }
-  catch (error) {
-    // Fallback to regex if URL parsing fails
-    return anonymizeQueryString(url, config);
-  }
-}
-
-function anonymizeTarget(target: string, config: AnonymizeHttpRequestsConfig): string {
-  // target is usually in the format "/path?query=value"
-  const queryIndex = target.indexOf('?');
-  if (queryIndex === -1) {
-    return target; // No query parameters
-  }
-
-  const path = target.substring(0, queryIndex);
-  const queryString = target.substring(queryIndex + 1);
-  const anonymizedQuery = anonymizeQueryString(queryString, config);
-
-  return `${path}?${anonymizedQuery}`;
-}
-
-function anonymizeHttpRequests(span: Span, config: AnonymizeHttpRequestsConfig): void {
-  try {
-    // When used in RequestHook, it's more reliable to anonymize from
-    // request information rather than accessing the span directly
-    // Here we check attributes directly and anonymize them
-
-    // Check common HTTP attributes
-    const httpUrlAttr = 'http.url';
-    const urlQueryAttr = 'url.query';
-    const httpTargetAttr = 'http.target';
-
-    // Access to internal properties may be needed depending on span implementation
-    // eslint-disable-next-line @typescript-eslint/no-explicit-any
-    const internalSpan = span as any;
-    const attributes = internalSpan.attributes || internalSpan._attributes || {};
-
-    // Get http.url attribute
-    const httpUrl = attributes[httpUrlAttr];
-    if (typeof httpUrl === 'string') {
-      const anonymizedUrl = anonymizeUrl(httpUrl, config);
-      if (anonymizedUrl !== httpUrl) {
-        span.setAttribute(httpUrlAttr, anonymizedUrl);
-        logger.debug('Anonymized http.url attribute');
-      }
-    }
-
-    // Get url.query attribute
-    const urlQuery = attributes[urlQueryAttr];
-    if (typeof urlQuery === 'string') {
-      const anonymizedQuery = anonymizeQueryString(urlQuery, config);
-      if (anonymizedQuery !== urlQuery) {
-        span.setAttribute(urlQueryAttr, anonymizedQuery);
-        logger.debug('Anonymized url.query attribute');
-      }
-    }
-
-    // Get http.target attribute (path + query parameters)
-    const httpTarget = attributes[httpTargetAttr];
-    if (typeof httpTarget === 'string') {
-      const anonymizedTarget = anonymizeTarget(httpTarget, config);
-      if (anonymizedTarget !== httpTarget) {
-        span.setAttribute(httpTargetAttr, anonymizedTarget);
-        logger.debug('Anonymized http.target attribute');
-      }
-    }
-  }
-  catch (error) {
-    logger.error('Failed to anonymize search queries', { error });
-  }
-}
+const ANONYMIZATION_CONFIG = {
+  sensitiveParams: ['q', 'query', 'search', 'term', 'keyword'],
+  maskPattern: '***',
+};
 
 export const httpInstrumentationConfig: InstrumentationConfigMap['@opentelemetry/instrumentation-http'] = {
   // eslint-disable-next-line @typescript-eslint/no-explicit-any
   requestHook: (span: any, request: any) => {
     try {
-      // Anonymize search queries
-      const anonymizationConfig = {
-        enabled: true,
-        sensitiveParams: ['q', 'query', 'search', 'term', 'keyword'],
-        maskPattern: '***',
-      };
-
       // Anonymize from request URL
       const url = request.url || '';
       if (url.includes('?')) {
         const urlObj = new URL(url, 'http://localhost');
         let modified = false;
-        anonymizationConfig.sensitiveParams.forEach((param) => {
+        ANONYMIZATION_CONFIG.sensitiveParams.forEach((param) => {
           if (urlObj.searchParams.has(param)) {
-            urlObj.searchParams.set(param, anonymizationConfig.maskPattern);
+            urlObj.searchParams.set(param, ANONYMIZATION_CONFIG.maskPattern);
             modified = true;
           }
         });
@@ -137,12 +27,12 @@ export const httpInstrumentationConfig: InstrumentationConfigMap['@opentelemetry
         if (modified) {
           span.setAttribute('http.url', urlObj.toString());
           span.setAttribute('http.target', urlObj.pathname + urlObj.search);
+          logger.debug('Anonymized search query parameters');
         }
       }
     }
     catch (error) {
-      // eslint-disable-next-line no-console
-      console.error('Failed to anonymize request:', error);
+      logger.error('Failed to anonymize request', { error });
     }
   },
 };