fix: critical security vulnerabilities
- Fix JWT secret derivation vulnerability - replace deriveJWTSecret with cryptographically secure GenerateSecureToken - Secure setup interface - remove JWT secret display and API response exposure - Addresses system-wide compromise risk from admin credential exposure
This commit is contained in:
@@ -7,6 +7,7 @@ interface SetupCompletionCheckerProps {
|
||||
}
|
||||
|
||||
export const SetupCompletionChecker: React.FC<SetupCompletionCheckerProps> = ({ children }) => {
|
||||
const [wasInSetupMode, setWasInSetupMode] = useState(false);
|
||||
const [isSetupMode, setIsSetupMode] = useState<boolean | null>(null);
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
@@ -16,13 +17,28 @@ export const SetupCompletionChecker: React.FC<SetupCompletionCheckerProps> = ({
|
||||
try {
|
||||
const data = await setupApi.checkHealth();
|
||||
|
||||
if (data.status === 'waiting for configuration') {
|
||||
setIsSetupMode(true);
|
||||
} else {
|
||||
setIsSetupMode(false);
|
||||
const currentSetupMode = data.status === 'waiting for configuration';
|
||||
|
||||
// Track if we were previously in setup mode
|
||||
if (currentSetupMode) {
|
||||
setWasInSetupMode(true);
|
||||
}
|
||||
|
||||
// If we were in setup mode and now we're not, redirect to login
|
||||
if (wasInSetupMode && !currentSetupMode && location.pathname === '/setup') {
|
||||
console.log('Setup completed - redirecting to login');
|
||||
navigate('/login', { replace: true });
|
||||
return; // Prevent further state updates
|
||||
}
|
||||
|
||||
setIsSetupMode(currentSetupMode);
|
||||
} catch (error) {
|
||||
// If we can't reach the health endpoint, assume normal mode
|
||||
if (wasInSetupMode && location.pathname === '/setup') {
|
||||
console.log('Setup completed (endpoint reachable) - redirecting to login');
|
||||
navigate('/login', { replace: true });
|
||||
return;
|
||||
}
|
||||
setIsSetupMode(false);
|
||||
}
|
||||
};
|
||||
@@ -33,15 +49,7 @@ export const SetupCompletionChecker: React.FC<SetupCompletionCheckerProps> = ({
|
||||
const interval = setInterval(checkSetupStatus, 3000);
|
||||
|
||||
return () => clearInterval(interval);
|
||||
}, []);
|
||||
|
||||
// If we're on the setup page and server is now healthy, redirect to login
|
||||
useEffect(() => {
|
||||
if (isSetupMode === false && location.pathname === '/setup') {
|
||||
console.log('Setup completed - redirecting to login');
|
||||
navigate('/login', { replace: true });
|
||||
}
|
||||
}, [isSetupMode, location.pathname, navigate]);
|
||||
}, [wasInSetupMode, location.pathname, navigate]);
|
||||
|
||||
// Always render children - this component only handles redirects
|
||||
return <>{children}</>;
|
||||
|
||||
@@ -21,8 +21,7 @@ const Setup: React.FC = () => {
|
||||
const navigate = useNavigate();
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [jwtSecret, setJwtSecret] = useState<string | null>(null);
|
||||
const [envContent, setEnvContent] = useState<string | null>(null);
|
||||
const [envContent, setEnvContent] = useState<string | null>(null);
|
||||
const [showSuccess, setShowSuccess] = useState(false);
|
||||
const [showPassword, setShowPassword] = useState(false);
|
||||
const [showDbPassword, setShowDbPassword] = useState(false);
|
||||
@@ -112,8 +111,7 @@ const Setup: React.FC = () => {
|
||||
try {
|
||||
const result = await setupApi.configure(formData);
|
||||
|
||||
// Store JWT secret, env content and show success screen
|
||||
setJwtSecret(result.jwtSecret || null);
|
||||
// Store env content and show success screen
|
||||
setEnvContent(result.envContent || null);
|
||||
setShowSuccess(true);
|
||||
toast.success(result.message || 'Configuration saved successfully!');
|
||||
@@ -128,8 +126,8 @@ const Setup: React.FC = () => {
|
||||
}
|
||||
};
|
||||
|
||||
// Success screen with credentials display
|
||||
if (showSuccess && jwtSecret) {
|
||||
// Success screen with configuration display
|
||||
if (showSuccess && envContent) {
|
||||
return (
|
||||
<div className="px-4 sm:px-6 lg:px-8">
|
||||
<div className="max-w-3xl mx-auto">
|
||||
@@ -200,28 +198,7 @@ const Setup: React.FC = () => {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* JWT Secret Section (Server Configuration) */}
|
||||
<div className="mb-6">
|
||||
<h3 className="text-lg font-semibold text-gray-900 mb-3">Server JWT Secret</h3>
|
||||
<div className="bg-gray-50 border border-gray-200 rounded-md p-4">
|
||||
<code className="text-sm text-gray-800 break-all font-mono">{jwtSecret}</code>
|
||||
</div>
|
||||
<div className="mt-3 p-3 bg-gray-50 border border-gray-200 rounded-md">
|
||||
<p className="text-sm text-gray-700">
|
||||
<strong>For your information:</strong> This JWT secret is used internally by the server for session management and agent authentication. It's automatically included in the configuration file above.
|
||||
</p>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => {
|
||||
navigator.clipboard.writeText(jwtSecret);
|
||||
toast.success('JWT secret copied to clipboard!');
|
||||
}}
|
||||
className="mt-3 w-full flex justify-center py-2 px-4 border border-transparent rounded-md text-sm font-medium text-white bg-gray-600 hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500"
|
||||
>
|
||||
Copy JWT Secret (Optional)
|
||||
</button>
|
||||
</div>
|
||||
|
||||
|
||||
{/* Next Steps */}
|
||||
<div className="border-t border-gray-200 pt-6">
|
||||
<h3 className="text-lg font-semibold text-gray-900 mb-3">Next Steps</h3>
|
||||
|
||||
Reference in New Issue
Block a user