From 5377a0f465c833d8de513cfe0d2ed6c55d97149e Mon Sep 17 00:00:00 2001 From: Koncept Kit <63216427+konceptkit@users.noreply.github.com> Date: Wed, 7 Jan 2026 14:03:32 +0700 Subject: [PATCH] Security Hardening --- src/components/IdleSessionWarning.js | 5 ++- src/context/AuthContext.js | 23 +++++----- src/utils/logger.js | 66 ++++++++++++++++++++++++++++ 3 files changed, 81 insertions(+), 13 deletions(-) create mode 100644 src/utils/logger.js diff --git a/src/components/IdleSessionWarning.js b/src/components/IdleSessionWarning.js index 1b6763f..adf450c 100644 --- a/src/components/IdleSessionWarning.js +++ b/src/components/IdleSessionWarning.js @@ -1,6 +1,7 @@ import React, { useState, useEffect, useCallback, useRef } from 'react'; import { useNavigate } from 'react-router-dom'; import { useAuth } from '../context/AuthContext'; +import logger from '../utils/logger'; import { Dialog, DialogContent, @@ -108,9 +109,9 @@ const IdleSessionWarning = () => { // Reset activity timer resetActivityTimer(); - console.log('[IdleSessionWarning] Session extended successfully'); + logger.log('[IdleSessionWarning] Session extended successfully'); } catch (error) { - console.error('[IdleSessionWarning] Failed to extend session:', error); + logger.error('[IdleSessionWarning] Failed to extend session:', error); // If refresh fails, logout handleSessionExpired(); diff --git a/src/context/AuthContext.js b/src/context/AuthContext.js index f05e6d8..5dae249 100644 --- a/src/context/AuthContext.js +++ b/src/context/AuthContext.js @@ -1,13 +1,14 @@ import React, { createContext, useState, useContext, useEffect } from 'react'; import axios from 'axios'; import api from '../utils/api'; +import logger from '../utils/logger'; const AuthContext = createContext(); const API_URL = process.env.REACT_APP_BACKEND_URL || window.location.origin; // Log environment on module load for debugging -console.log('[AuthContext] Module initialized with:', { +logger.log('[AuthContext] Module initialized with:', { REACT_APP_BACKEND_URL: process.env.REACT_APP_BACKEND_URL, REACT_APP_BASENAME: process.env.REACT_APP_BASENAME, API_URL: API_URL @@ -56,14 +57,14 @@ export const AuthProvider = ({ children }) => { }); setPermissions(response.data.permissions || []); } catch (error) { - console.error('Failed to fetch permissions:', error); + logger.error('Failed to fetch permissions:', error); setPermissions([]); } }; const login = async (email, password) => { try { - console.log('[AuthContext] Starting login request...', { + logger.log('[AuthContext] Starting login request...', { API_URL: API_URL, envBackendUrl: process.env.REACT_APP_BACKEND_URL, fullUrl: `${API_URL}/api/auth/login` @@ -80,7 +81,7 @@ export const AuthProvider = ({ children }) => { } ); - console.log('[AuthContext] Login response received:', { + logger.log('[AuthContext] Login response received:', { status: response.status, hasToken: !!response.data?.access_token, hasUser: !!response.data?.user @@ -98,23 +99,23 @@ export const AuthProvider = ({ children }) => { if (storedToken !== access_token) { throw new Error('Failed to store token in localStorage'); } - console.log('[AuthContext] Token stored and verified in localStorage'); + logger.log('[AuthContext] Token stored and verified in localStorage'); // Update state in correct order setToken(access_token); setUser(userData); - console.log('[AuthContext] User state updated:', { + logger.log('[AuthContext] User state updated:', { email: userData.email, role: userData.role }); // Fetch permissions immediately and WAIT for it (but don't fail login if it fails) try { - console.log('[AuthContext] Fetching permissions...'); + logger.log('[AuthContext] Fetching permissions...'); await fetchPermissions(access_token); - console.log('[AuthContext] Permissions fetched successfully'); + logger.log('[AuthContext] Permissions fetched successfully'); } catch (permError) { - console.error('[AuthContext] Failed to fetch permissions (non-critical):', { + logger.error('[AuthContext] Failed to fetch permissions (non-critical):', { message: permError.message, response: permError.response?.data, status: permError.response?.status @@ -127,7 +128,7 @@ export const AuthProvider = ({ children }) => { return userData; } catch (error) { // Enhanced error logging - console.error('[AuthContext] Login failed:', { + logger.error('[AuthContext] Login failed:', { message: error.message, response: error.response?.data, status: error.response?.status, @@ -174,7 +175,7 @@ export const AuthProvider = ({ children }) => { setUser(response.data); return response.data; } catch (error) { - console.error('Failed to refresh user:', error); + logger.error('Failed to refresh user:', error); // If token expired, logout if (error.response?.status === 401) { logout(); diff --git a/src/utils/logger.js b/src/utils/logger.js new file mode 100644 index 0000000..608c442 --- /dev/null +++ b/src/utils/logger.js @@ -0,0 +1,66 @@ +/** + * Production-safe logging utility + * + * In production (NODE_ENV=production), logs are disabled by default + * to prevent exposing sensitive information in browser console. + * + * In development, all logs are shown for debugging. + * + * Usage: + * import logger from '../utils/logger'; + * logger.log('[Component]', 'message', data); + * logger.error('[Component]', 'error message', error); + * logger.warn('[Component]', 'warning message'); + */ + +const isDevelopment = process.env.NODE_ENV === 'development'; + +// Force enable logs with REACT_APP_DEBUG_LOGS=true in .env +const debugEnabled = process.env.REACT_APP_DEBUG_LOGS === 'true'; + +const shouldLog = isDevelopment || debugEnabled; + +const logger = { + log: (...args) => { + if (shouldLog) { + console.log(...args); + } + }, + + error: (...args) => { + // Always log errors, but sanitize in production + if (shouldLog) { + console.error(...args); + } else { + // In production, only log error type without details + console.error('An error occurred. Enable debug logs for details.'); + } + }, + + warn: (...args) => { + if (shouldLog) { + console.warn(...args); + } + }, + + info: (...args) => { + if (shouldLog) { + console.info(...args); + } + }, + + debug: (...args) => { + if (shouldLog) { + console.debug(...args); + } + }, + + // Special method for sensitive data - NEVER logs in production + sensitive: (...args) => { + if (isDevelopment) { + console.log('[SENSITIVE]', ...args); + } + } +}; + +export default logger;