Fixes config.json exposure
This commit is contained in:
2
public/.gitkeep
Normal file
2
public/.gitkeep
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
# This file ensures the public directory is kept in git
|
||||||
|
# Public directory is for static assets that should be served to browsers
|
||||||
@@ -190,7 +190,7 @@ export const Dashboard: React.FC<DashboardProps> = () => {
|
|||||||
<span className="text-sm text-ctp-text hidden sm:inline">Settings</span>
|
<span className="text-sm text-ctp-text hidden sm:inline">Settings</span>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={fetchWorkflowRuns}
|
onClick={() => fetchWorkflowRuns()}
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
className="flex items-center space-x-2 px-3 md:px-4 py-2 bg-ctp-blue text-ctp-base rounded-lg hover:bg-ctp-lavender disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
|
className="flex items-center space-x-2 px-3 md:px-4 py-2 bg-ctp-blue text-ctp-base rounded-lg hover:bg-ctp-lavender disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -99,7 +99,11 @@ describe('ApiService', () => {
|
|||||||
name: 'test-repo',
|
name: 'test-repo',
|
||||||
full_name: 'test-owner/test-repo'
|
full_name: 'test-owner/test-repo'
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
cache: {
|
||||||
|
timeoutSeconds: 300
|
||||||
|
},
|
||||||
|
repositoryCount: 1
|
||||||
};
|
};
|
||||||
|
|
||||||
mockedAxios.get.mockResolvedValueOnce({ data: mockConfig });
|
mockedAxios.get.mockResolvedValueOnce({ data: mockConfig });
|
||||||
@@ -108,6 +112,9 @@ describe('ApiService', () => {
|
|||||||
|
|
||||||
expect(mockedAxios.get).toHaveBeenCalledWith('/api/config');
|
expect(mockedAxios.get).toHaveBeenCalledWith('/api/config');
|
||||||
expect(result).toEqual(mockConfig);
|
expect(result).toEqual(mockConfig);
|
||||||
|
// Ensure sensitive data is not included
|
||||||
|
expect(result).not.toHaveProperty('github.token');
|
||||||
|
expect(result).not.toHaveProperty('server');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,53 @@
|
|||||||
import { defineConfig } from 'vite'
|
import { defineConfig } from 'vite'
|
||||||
import react from '@vitejs/plugin-react'
|
import react from '@vitejs/plugin-react'
|
||||||
|
|
||||||
|
// Security plugin to block sensitive files
|
||||||
|
const securityPlugin = () => {
|
||||||
|
return {
|
||||||
|
name: 'security-plugin',
|
||||||
|
configureServer(server: any) {
|
||||||
|
server.middlewares.use((req: any, res: any, next: any) => {
|
||||||
|
const url = req.url?.toLowerCase() || '';
|
||||||
|
|
||||||
|
// Only block the most critical sensitive files
|
||||||
|
const blockedFiles = [
|
||||||
|
'/config.json',
|
||||||
|
'/config.example.json',
|
||||||
|
'/.env'
|
||||||
|
];
|
||||||
|
|
||||||
|
// Block specific directory traversal attempts
|
||||||
|
const blockedPaths = [
|
||||||
|
'/server/',
|
||||||
|
'/.git/'
|
||||||
|
];
|
||||||
|
|
||||||
|
// Check for exact file matches
|
||||||
|
const isBlockedFile = blockedFiles.includes(url);
|
||||||
|
|
||||||
|
// Check for blocked directory access
|
||||||
|
const isBlockedPath = blockedPaths.some(path => url.startsWith(path));
|
||||||
|
|
||||||
|
if (isBlockedFile || isBlockedPath) {
|
||||||
|
console.warn(`🚫 Blocked access to sensitive file: ${req.url} from ${req.headers['x-forwarded-for'] || req.socket.remoteAddress}`);
|
||||||
|
res.statusCode = 403;
|
||||||
|
res.setHeader('Content-Type', 'application/json');
|
||||||
|
res.end(JSON.stringify({
|
||||||
|
error: 'Access denied',
|
||||||
|
message: 'This resource is not available'
|
||||||
|
}));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [react()],
|
plugins: [react(), securityPlugin()],
|
||||||
|
publicDir: 'public',
|
||||||
server: {
|
server: {
|
||||||
host: '0.0.0.0',
|
host: '0.0.0.0',
|
||||||
port: 3000,
|
port: 3000,
|
||||||
@@ -11,4 +56,13 @@ export default defineConfig({
|
|||||||
'/api': 'http://localhost:3001'
|
'/api': 'http://localhost:3001'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
build: {
|
||||||
|
rollupOptions: {
|
||||||
|
// These files should not be bundled into the build
|
||||||
|
external: (id) => {
|
||||||
|
// Only externalize if it's exactly these files
|
||||||
|
return id === 'config.json' || id === '.env' || id === 'config.example.json'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
Reference in New Issue
Block a user