Compare commits
	
		
			9 commits
		
	
	
		
			4a1187c3a9
			...
			fb1e39ed1c
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | fb1e39ed1c | ||
|  | e58620fc42 | ||
|  | b677352f4f | ||
|  | 9caedb4480 | ||
|  | 59832d8e11 | ||
|  | 601a7acb16 | ||
|  | 9bee70c106 | ||
|  | b2250966fb | ||
|  | 43510fb367 | 
							
								
								
									
										10
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								README.md
									
									
									
									
									
								
							|  | @ -1,5 +1,15 @@ | ||||||
| # CSPJ Application | # CSPJ Application | ||||||
| 
 | 
 | ||||||
|  | ## Setup | ||||||
|  | 
 | ||||||
|  | ### Requirements | ||||||
|  | 
 | ||||||
|  | User to be added into `docker` group. | ||||||
|  | 
 | ||||||
|  | ```bash | ||||||
|  | sudo usermod -aG docker $USER | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
| ## Services | ## Services | ||||||
| 
 | 
 | ||||||
| - 3331: Apache + ModSecurity | - 3331: Apache + ModSecurity | ||||||
|  |  | ||||||
|  | @ -2,7 +2,6 @@ import { Box, Stack } from "@mui/material"; | ||||||
| import { WindowButtons } from "./WindowButtons"; | import { WindowButtons } from "./WindowButtons"; | ||||||
| import { NavigationButtons } from "./NavigationButtons"; | import { NavigationButtons } from "./NavigationButtons"; | ||||||
| import { RouteDisplay } from "./RouteDisplay"; | import { RouteDisplay } from "./RouteDisplay"; | ||||||
| import { Testing } from "../Testing/Testing"; |  | ||||||
| import { ServerStatus } from "./ServerStatus"; | import { ServerStatus } from "./ServerStatus"; | ||||||
| 
 | 
 | ||||||
| export const HeaderBar = () => { | export const HeaderBar = () => { | ||||||
|  | @ -73,7 +72,6 @@ export const HeaderBar = () => { | ||||||
|           }} |           }} | ||||||
|         > |         > | ||||||
|           <ServerStatus /> |           <ServerStatus /> | ||||||
|           <Testing /> |  | ||||||
|           <WindowButtons /> |           <WindowButtons /> | ||||||
|         </Stack> |         </Stack> | ||||||
|       </Box> |       </Box> | ||||||
|  |  | ||||||
|  | @ -1,7 +1,7 @@ | ||||||
| import { Box, Button, Chip, CircularProgress, Popover, Stack } from "@mui/material"; | import { Box, Button, Chip, CircularProgress, Popover, Stack } from "@mui/material"; | ||||||
| import { fetch } from "@tauri-apps/plugin-http"; | import { fetch } from "@tauri-apps/plugin-http"; | ||||||
| import { useAtom } from "jotai"; | import { useAtom } from "jotai"; | ||||||
| import { MouseEvent, useEffect, useState } from "react"; | import { MouseEvent, useCallback, useEffect, useState } from "react"; | ||||||
| import { serverConnectionAtom, serverUrlAtom } from "../../lib/jotai"; | import { serverConnectionAtom, serverUrlAtom } from "../../lib/jotai"; | ||||||
| import { defaultSettings } from "../../lib/settings"; | import { defaultSettings } from "../../lib/settings"; | ||||||
| import { ServerUrlInput } from "./ServerUrlInput"; | import { ServerUrlInput } from "./ServerUrlInput"; | ||||||
|  | @ -24,7 +24,7 @@ export const ServerStatus = () => { | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   // function to check server health
 |   // function to check server health
 | ||||||
|   const checkServerConnection = async () => { |   const checkServerConnection = useCallback(async () => { | ||||||
|     setServerConnection("connecting"); |     setServerConnection("connecting"); | ||||||
| 
 | 
 | ||||||
|     // remove trailing slash
 |     // remove trailing slash
 | ||||||
|  | @ -42,7 +42,7 @@ export const ServerStatus = () => { | ||||||
|       await sleep(500); |       await sleep(500); | ||||||
|       setServerConnection("disconnected"); |       setServerConnection("disconnected"); | ||||||
|     } |     } | ||||||
|   }; |   }, [serverUrl, setServerConnection, setServerUrl]); | ||||||
| 
 | 
 | ||||||
|   const chipProps = { |   const chipProps = { | ||||||
|     color: serverConnection === "connected" ? "success" : serverConnection === "disconnected" ? "error" : "warning", |     color: serverConnection === "connected" ? "success" : serverConnection === "disconnected" ? "error" : "warning", | ||||||
|  | @ -64,7 +64,7 @@ export const ServerStatus = () => { | ||||||
|         clearInterval(intervalId); |         clearInterval(intervalId); | ||||||
|       }; |       }; | ||||||
|     } |     } | ||||||
|   }, [serverConnection, serverUrl]); |   }, [checkServerConnection, serverConnection, serverUrl]); | ||||||
| 
 | 
 | ||||||
|   return ( |   return ( | ||||||
|     <Box |     <Box | ||||||
|  |  | ||||||
							
								
								
									
										28
									
								
								client/src/components/Pages/SqlInjection/SetupLogItem.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								client/src/components/Pages/SqlInjection/SetupLogItem.tsx
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,28 @@ | ||||||
|  | import { Grid2 } from "@mui/material"; | ||||||
|  | import { FC } from "react"; | ||||||
|  | 
 | ||||||
|  | type SetupLogItemProps = { | ||||||
|  |   children?: string; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | export const SetupLogItem: FC<SetupLogItemProps> = ({ children }) => { | ||||||
|  |   return ( | ||||||
|  |     <Grid2 | ||||||
|  |       color="lightgreen" | ||||||
|  |       container | ||||||
|  |       fontFamily="JetBrainsMono" | ||||||
|  |       fontSize="0.9rem" | ||||||
|  |       spacing={1} | ||||||
|  |     > | ||||||
|  |       <Grid2 size="auto">></Grid2> | ||||||
|  |       <Grid2 | ||||||
|  |         size="grow" | ||||||
|  |         sx={{ | ||||||
|  |           wordBreak: "break-word", | ||||||
|  |         }} | ||||||
|  |       > | ||||||
|  |         {children} | ||||||
|  |       </Grid2> | ||||||
|  |     </Grid2> | ||||||
|  |   ); | ||||||
|  | }; | ||||||
|  | @ -3,13 +3,14 @@ import { Box, Divider, Tab, useTheme } from "@mui/material"; | ||||||
| import { useState } from "react"; | import { useState } from "react"; | ||||||
| import { SqlInjectionLogin } from "./SqlInjectionLogin"; | import { SqlInjectionLogin } from "./SqlInjectionLogin"; | ||||||
| import { SqlInjectionRegister } from "./SqlInjectionRegister"; | import { SqlInjectionRegister } from "./SqlInjectionRegister"; | ||||||
|  | import { SqlInjectionSetup } from "./SqlInjectionSetup"; | ||||||
| 
 | 
 | ||||||
| export const SqlInjection = () => { | export const SqlInjection = () => { | ||||||
|   // contexts
 |   // contexts
 | ||||||
|   const theme = useTheme(); |   const theme = useTheme(); | ||||||
| 
 | 
 | ||||||
|   // states
 |   // states
 | ||||||
|   const [subTabValue, setSubTabValue] = useState("register"); |   const [subTabValue, setSubTabValue] = useState("setup"); | ||||||
| 
 | 
 | ||||||
|   // logic for switching tabs
 |   // logic for switching tabs
 | ||||||
|   const subTabChangeEvent = (newTabValue: string) => { |   const subTabChangeEvent = (newTabValue: string) => { | ||||||
|  | @ -37,7 +38,7 @@ export const SqlInjection = () => { | ||||||
|       > |       > | ||||||
|         <TabContext value={subTabValue}> |         <TabContext value={subTabValue}> | ||||||
|           <TabList |           <TabList | ||||||
|             onChange={(e, value) => { |             onChange={(_, value) => { | ||||||
|               subTabChangeEvent(value); |               subTabChangeEvent(value); | ||||||
|             }} |             }} | ||||||
|             scrollButtons={true} |             scrollButtons={true} | ||||||
|  | @ -46,6 +47,10 @@ export const SqlInjection = () => { | ||||||
|             }} |             }} | ||||||
|             variant="standard" |             variant="standard" | ||||||
|           > |           > | ||||||
|  |             <Tab | ||||||
|  |               label="Setup" | ||||||
|  |               value="setup" | ||||||
|  |             /> | ||||||
|             <Tab |             <Tab | ||||||
|               label="Register" |               label="Register" | ||||||
|               value="register" |               value="register" | ||||||
|  | @ -66,6 +71,12 @@ export const SqlInjection = () => { | ||||||
|               width: "100%", |               width: "100%", | ||||||
|             }} |             }} | ||||||
|           > |           > | ||||||
|  |             <TabPanel | ||||||
|  |               sx={{ p: 2 }} | ||||||
|  |               value="setup" | ||||||
|  |             > | ||||||
|  |               <SqlInjectionSetup /> | ||||||
|  |             </TabPanel> | ||||||
|             <TabPanel |             <TabPanel | ||||||
|               sx={{ p: 2 }} |               sx={{ p: 2 }} | ||||||
|               value="register" |               value="register" | ||||||
|  |  | ||||||
							
								
								
									
										120
									
								
								client/src/components/Pages/SqlInjection/SqlInjectionSetup.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										120
									
								
								client/src/components/Pages/SqlInjection/SqlInjectionSetup.tsx
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,120 @@ | ||||||
|  | import { LoadingButton } from "@mui/lab"; | ||||||
|  | import { Box, FormControlLabel, Grid, Grid2, Switch, TextField, Typography } from "@mui/material"; | ||||||
|  | import { fetch } from "@tauri-apps/plugin-http"; | ||||||
|  | import { useAtom } from "jotai"; | ||||||
|  | import { useState } from "react"; | ||||||
|  | import { useNotification } from "../../../contexts/NotificationContext"; | ||||||
|  | import { serverUrlAtom } from "../../../lib/jotai"; | ||||||
|  | import { HeaderLogo } from "../../Generic/HeaderLogo"; | ||||||
|  | import { SetupLogItem } from "./SetupLogItem"; | ||||||
|  | 
 | ||||||
|  | export const SqlInjectionSetup = () => { | ||||||
|  |   // contexts
 | ||||||
|  |   const { openNotification } = useNotification(); | ||||||
|  | 
 | ||||||
|  |   // atoms
 | ||||||
|  |   const [serverUrl, setServerUrl] = useAtom(serverUrlAtom); | ||||||
|  | 
 | ||||||
|  |   // states
 | ||||||
|  |   const [logs, setLogs] = useState<string[]>([]); | ||||||
|  |   const [loading, setLoading] = useState<boolean>(false); | ||||||
|  | 
 | ||||||
|  |   const setupClickEvent = async () => { | ||||||
|  |     setLoading(true); | ||||||
|  | 
 | ||||||
|  |     try { | ||||||
|  |       const response = await fetch(serverUrl + "/nuke-db", { | ||||||
|  |         method: "POST", | ||||||
|  |       }); | ||||||
|  |       const responseText = await response.text(); | ||||||
|  |       if (!response.ok) { | ||||||
|  |         openNotification("Nuke failed: " + responseText); | ||||||
|  |       } else { | ||||||
|  |         openNotification("Nuke successful"); | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       setLogs((prevLogs) => [...prevLogs.slice(-5), responseText]); | ||||||
|  | 
 | ||||||
|  |       const response2 = await fetch(serverUrl + "/setup-demo-db", { | ||||||
|  |         method: "POST", | ||||||
|  |       }); | ||||||
|  |       const responseText2 = await response2.text(); | ||||||
|  |       if (!response.ok) { | ||||||
|  |         openNotification("Setup demo DB failed: " + responseText2); | ||||||
|  |       } else { | ||||||
|  |         openNotification("Demo database setup successful"); | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       setLogs((prevLogs) => [...prevLogs.slice(-5), responseText2]); | ||||||
|  |     } catch (e) { | ||||||
|  |       // log the error and handle failure
 | ||||||
|  |       console.log("Request failed", e); | ||||||
|  |     } finally { | ||||||
|  |       // stop loading indicator regardless of success/failure
 | ||||||
|  |       setLoading(false); | ||||||
|  |     } | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  |   return ( | ||||||
|  |     <> | ||||||
|  |       <Box | ||||||
|  |         alignItems="center" | ||||||
|  |         display="flex" | ||||||
|  |         flexDirection="row" | ||||||
|  |         sx={{ | ||||||
|  |           mb: 2, | ||||||
|  |         }} | ||||||
|  |       > | ||||||
|  |         <HeaderLogo /> | ||||||
|  |         <Typography | ||||||
|  |           sx={{ | ||||||
|  |             color: "grey", | ||||||
|  |           }} | ||||||
|  |           variant="h6" | ||||||
|  |         > | ||||||
|  |            Setup | ||||||
|  |         </Typography> | ||||||
|  |       </Box> | ||||||
|  |       <Box | ||||||
|  |         sx={{ | ||||||
|  |           display: "flex", | ||||||
|  |           flexDirection: "column", | ||||||
|  |           maxHeight: "100%", | ||||||
|  |         }} | ||||||
|  |       > | ||||||
|  |         <Typography variant="body2"> | ||||||
|  |           Click on the 'Setup/reset DB' button below to initialize or reset your database. | ||||||
|  |         </Typography> | ||||||
|  |         <Typography variant="body2"> | ||||||
|  |           If you get an error, make sure you have set the correct server URL (check the chip at the top right). | ||||||
|  |         </Typography> | ||||||
|  |         <Typography variant="body2"> | ||||||
|  |           Demo email:{" "} | ||||||
|  |           <b> | ||||||
|  |             <code style={{ fontFamily: "JetBrainsMono" }}>asdf@gmail.com</code> | ||||||
|  |           </b> | ||||||
|  |         </Typography> | ||||||
|  |         <Typography variant="body2"> | ||||||
|  |           Demo password:{" "} | ||||||
|  |           <b> | ||||||
|  |             <code style={{ fontFamily: "JetBrainsMono" }}>asdf</code> | ||||||
|  |           </b> | ||||||
|  |         </Typography> | ||||||
|  |         <LoadingButton | ||||||
|  |           loading={loading} | ||||||
|  |           onClick={setupClickEvent} | ||||||
|  |           size="small" | ||||||
|  |           sx={{ | ||||||
|  |             my: 2, | ||||||
|  |           }} | ||||||
|  |           variant="contained" | ||||||
|  |         > | ||||||
|  |           Setup/reset DB | ||||||
|  |         </LoadingButton> | ||||||
|  |         {logs.map((log, index) => ( | ||||||
|  |           <SetupLogItem key={index}>{log}</SetupLogItem> | ||||||
|  |         ))} | ||||||
|  |       </Box> | ||||||
|  |     </> | ||||||
|  |   ); | ||||||
|  | }; | ||||||
|  | @ -1,10 +0,0 @@ | ||||||
| import { Box } from "@mui/material"; |  | ||||||
| 
 |  | ||||||
| export const Xss = () => { |  | ||||||
|   return ( |  | ||||||
|     <Box> |  | ||||||
|       <h1>XSS</h1> |  | ||||||
|       <p>Cross-site scripting (XSS) is a type of security vulnerability</p> |  | ||||||
|     </Box> |  | ||||||
|   ); |  | ||||||
| }; |  | ||||||
|  | @ -1,74 +0,0 @@ | ||||||
| import { BugReportOutlined } from "@mui/icons-material"; |  | ||||||
| import { Box, Button, IconButton, useTheme } from "@mui/material"; |  | ||||||
| import { fetch } from "@tauri-apps/plugin-http"; |  | ||||||
| import { useAtom } from "jotai"; |  | ||||||
| import { useState } from "react"; |  | ||||||
| import { serverUrlAtom } from "../../lib/jotai"; |  | ||||||
| import { defaultSettings } from "../../lib/settings"; |  | ||||||
| import { FloatingDialog } from "../Generic/FloatingDialog"; |  | ||||||
| 
 |  | ||||||
| export const Testing = () => { |  | ||||||
|   // contexts
 |  | ||||||
|   const theme = useTheme(); |  | ||||||
| 
 |  | ||||||
|   // atoms
 |  | ||||||
|   const [serverUrl, setServerUrl] = useAtom(serverUrlAtom); |  | ||||||
| 
 |  | ||||||
|   // states
 |  | ||||||
|   const [openState, setOpenState] = useState(false); |  | ||||||
|   const [maximisedState, setMaximisedState] = useState(false); |  | ||||||
| 
 |  | ||||||
|   // functions
 |  | ||||||
|   const close = () => setOpenState(false); |  | ||||||
| 
 |  | ||||||
|   const testing = () => { |  | ||||||
|     fetch(serverUrl + "/nuke-db").then((response) => { |  | ||||||
|       console.log(response); |  | ||||||
|     }); |  | ||||||
|     fetch(serverUrl + "/setup-demo-db").then((response) => { |  | ||||||
|       console.log(response); |  | ||||||
|     }); |  | ||||||
|   }; |  | ||||||
| 
 |  | ||||||
|   return ( |  | ||||||
|     <FloatingDialog |  | ||||||
|       body={ |  | ||||||
|         <Box |  | ||||||
|           sx={{ |  | ||||||
|             border: "1px solid " + theme.palette.grey[700], |  | ||||||
|             borderRadius: defaultSettings.style.radius + "px", |  | ||||||
|             display: "flex", |  | ||||||
|             flexDirection: "column", |  | ||||||
|             flexGrow: 1, |  | ||||||
|             my: 2, |  | ||||||
|             overflow: "hidden", |  | ||||||
|             p: 0, |  | ||||||
|           }} |  | ||||||
|         > |  | ||||||
|           <Box> |  | ||||||
|             <Button |  | ||||||
|               onClick={() => { |  | ||||||
|                 testing(); |  | ||||||
|               }} |  | ||||||
|             > |  | ||||||
|               test |  | ||||||
|             </Button> |  | ||||||
|           </Box> |  | ||||||
|         </Box> |  | ||||||
|       } |  | ||||||
|       close={close} |  | ||||||
|       maximisedState={maximisedState} |  | ||||||
|       openButton={ |  | ||||||
|         <IconButton |  | ||||||
|           onClick={() => setOpenState(true)} |  | ||||||
|           size="small" |  | ||||||
|         > |  | ||||||
|           <BugReportOutlined /> |  | ||||||
|         </IconButton> |  | ||||||
|       } |  | ||||||
|       openState={openState} |  | ||||||
|       setMaximisedState={setMaximisedState} |  | ||||||
|       title="Testing" |  | ||||||
|     /> |  | ||||||
|   ); |  | ||||||
| }; |  | ||||||
|  | @ -2,6 +2,7 @@ services: | ||||||
|   postgres: |   postgres: | ||||||
|     image: postgres:latest |     image: postgres:latest | ||||||
|     container_name: postgres_db |     container_name: postgres_db | ||||||
|  |     restart: unless-stopped | ||||||
|     environment: |     environment: | ||||||
|       POSTGRES_USER: asdfuser |       POSTGRES_USER: asdfuser | ||||||
|       POSTGRES_PASSWORD: asdfpassword |       POSTGRES_PASSWORD: asdfpassword | ||||||
|  | @ -14,7 +15,7 @@ services: | ||||||
|   modsecurity: |   modsecurity: | ||||||
|     image: owasp/modsecurity-crs:apache-alpine |     image: owasp/modsecurity-crs:apache-alpine | ||||||
|     container_name: modsecurity |     container_name: modsecurity | ||||||
|     restart: always |     restart: unless-stopped | ||||||
|     environment: |     environment: | ||||||
|       BACKEND: "http://localhost:80" |       BACKEND: "http://localhost:80" | ||||||
|       SERVER_NAME: "localhost" |       SERVER_NAME: "localhost" | ||||||
|  | @ -36,9 +37,27 @@ services: | ||||||
|   dvwa: |   dvwa: | ||||||
|     image: vulnerables/web-dvwa |     image: vulnerables/web-dvwa | ||||||
|     container_name: dvwa |     container_name: dvwa | ||||||
|     restart: always |     restart: unless-stopped | ||||||
|     ports: |     ports: | ||||||
|       - "80:80" |       - "80:80" | ||||||
| 
 | 
 | ||||||
|  |   cspj-server: | ||||||
|  |     image: cspj-server | ||||||
|  |     container_name: cspj-server | ||||||
|  |     restart: unless-stopped | ||||||
|  |     network_mode: host | ||||||
|  |     depends_on: | ||||||
|  |       - postgres | ||||||
|  |     volumes: | ||||||
|  |       - "/home/vomitblood/build/cspj-application/docker/chungus/logs/:/tmp" | ||||||
|  |     command: ["server", "-l", "/tmp"] | ||||||
|  | 
 | ||||||
|  |   cspj-ml-server: | ||||||
|  |     image: cspj-ml-server | ||||||
|  |     container_name: cspj-ml-server | ||||||
|  |     restart: unless-stopped | ||||||
|  |     ports: | ||||||
|  |       - "5000:5000" | ||||||
|  | 
 | ||||||
| volumes: | volumes: | ||||||
|   postgres_data: |   postgres_data: | ||||||
|  |  | ||||||
							
								
								
									
										1
									
								
								docker/chungus/remove.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								docker/chungus/remove.sh
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1 @@ | ||||||
|  | docker container rm modsecurity suricata dvwa postgres_db | ||||||
|  | @ -1,7 +1,11 @@ | ||||||
| mkdir logs | mkdir logs | ||||||
|  | 
 | ||||||
| touch ./logs/host-fs-auditlog.log | touch ./logs/host-fs-auditlog.log | ||||||
| touch ./logs/host-fs-errorlog.log | touch ./logs/host-fs-errorlog.log | ||||||
| touch ./logs/host-fs-accesslog.log | touch ./logs/host-fs-accesslog.log | ||||||
|  | 
 | ||||||
| chmod 777 ./logs/host-fs-auditlog.log | chmod 777 ./logs/host-fs-auditlog.log | ||||||
| chmod 777 ./logs/host-fs-errorlog.log | chmod 777 ./logs/host-fs-errorlog.log | ||||||
| chmod 777 ./logs/host-fs-accesslog.log | chmod 777 ./logs/host-fs-accesslog.log | ||||||
|  | 
 | ||||||
|  | docker compose up | ||||||
|  | @ -14,5 +14,5 @@ services: | ||||||
|     volumes: |     volumes: | ||||||
|       - ./suricata/etc:/etc/suricata |       - ./suricata/etc:/etc/suricata | ||||||
|       - ./suricata/logs:/var/log/suricata |       - ./suricata/logs:/var/log/suricata | ||||||
|       - ./suricata/lib:/var/lib/suricata |       # - ./suricata/lib:/var/lib/suricata | ||||||
|     command: ["-c", "/etc/suricata/suricata.yaml", "--af-packet"] |     command: ["-c", "/etc/suricata/suricata.yaml", "--af-packet"] | ||||||
|  |  | ||||||
							
								
								
									
										11
									
								
								server-ml/Dockerfile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								server-ml/Dockerfile
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,11 @@ | ||||||
|  | FROM python:3.11.6-slim | ||||||
|  | 
 | ||||||
|  | WORKDIR /app | ||||||
|  | 
 | ||||||
|  | COPY requirements.txt . | ||||||
|  | 
 | ||||||
|  | RUN pip install --no-cache-dir -r requirements.txt | ||||||
|  | 
 | ||||||
|  | COPY main.py . | ||||||
|  | 
 | ||||||
|  | CMD ["python", "main.py"] | ||||||
|  | @ -0,0 +1,46 @@ | ||||||
|  | # Test Cases | ||||||
|  | 
 | ||||||
|  | ## 1. Basic test cases | ||||||
|  | 
 | ||||||
|  | [Good] `SELECT _ FROM users WHERE id = 1`   | ||||||
|  | [Bad] `SELECT _ FROM users WHERE id = 1 OR 1=1` | ||||||
|  | 
 | ||||||
|  | ## 2. Authentication bypass cases | ||||||
|  | 
 | ||||||
|  | [Bad] `SELECT _ FROM users WHERE username = 'admin' --`   | ||||||
|  | [Bad] `SELECT _ FROM users WHERE username = 'admin' #`   | ||||||
|  | [Bad] `SELECT \* FROM users WHERE username = 'admin' OR '1'='1'` | ||||||
|  | 
 | ||||||
|  | ## 3. Union based injection cases | ||||||
|  | 
 | ||||||
|  | [Bad] `SELECT id, username FROM users WHERE id = 1 UNION SELECT null, 'hacker'`   | ||||||
|  | [Bad] `SELECT id, username FROM users WHERE id = 1 UNION SELECT 1, 'hacked' FROM dual`   | ||||||
|  | [Bad] `SELECT database() UNION SELECT 1` | ||||||
|  | 
 | ||||||
|  | ## 4. Error based injection cases | ||||||
|  | 
 | ||||||
|  | [Bad] `SELECT _ FROM users WHERE id = 1 AND (SELECT 1 FROM users WHERE id=2)=1`   | ||||||
|  | [Bad] `SELECT _ FROM users WHERE id = (SELECT COUNT(\*) FROM users)` | ||||||
|  | 
 | ||||||
|  | ## 5. Blind SQL injection cases | ||||||
|  | 
 | ||||||
|  | [Bad] `SELECT _ FROM users WHERE id = 1; WAITFOR DELAY '00:00:10' --`   | ||||||
|  | [Bad] `SELECT _ FROM users WHERE username = 'admin' AND 1=1` | ||||||
|  | 
 | ||||||
|  | ## 6. Hex and Base64 encoded injection cases | ||||||
|  | 
 | ||||||
|  | [Bad] `SELECT _ FROM users WHERE username = 0x61646D696E`   | ||||||
|  | [Bad] `SELECT _ FROM users WHERE username = 'YWRtaW4='` | ||||||
|  | 
 | ||||||
|  | ## 7. False positives cases | ||||||
|  | 
 | ||||||
|  | [Good] `SELECT _ FROM users WHERE id = 5`   | ||||||
|  | [Good] `SELECT users.name, orders.amount FROM users JOIN orders ON users.id = orders.user_id`   | ||||||
|  | [Good] `SELECT _ FROM users WHERE username = ? AND password = ?` | ||||||
|  | 
 | ||||||
|  | ## 8. Edge cases | ||||||
|  | 
 | ||||||
|  | [Good] `""`   | ||||||
|  | [Bad] `'; --`   | ||||||
|  | [Good] `12345`   | ||||||
|  | [Good] `asdkjhasdkjh` | ||||||
							
								
								
									
										7
									
								
								server-ml/docker-compose.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								server-ml/docker-compose.yml
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,7 @@ | ||||||
|  | services: | ||||||
|  |   cspj-ml-server: | ||||||
|  |     image: cspj-ml-server | ||||||
|  |     container_name: cspj-ml-server | ||||||
|  |     restart: unless-stopped | ||||||
|  |     ports: | ||||||
|  |       - "5000:5000" | ||||||
|  | @ -2,19 +2,24 @@ from flask import Flask, request, jsonify | ||||||
| import torch | import torch | ||||||
| from transformers import MobileBertTokenizer, MobileBertForSequenceClassification | from transformers import MobileBertTokenizer, MobileBertForSequenceClassification | ||||||
| 
 | 
 | ||||||
| # Initialize Flask app | print("Starting server...") | ||||||
|  | 
 | ||||||
|  | # initialize Flask app | ||||||
| app = Flask(__name__) | app = Flask(__name__) | ||||||
| 
 | 
 | ||||||
| # Set device (GPU if available, otherwise CPU) | # set device, use gpu if available | ||||||
| device = torch.device("cuda" if torch.cuda.is_available() else "cpu") | device = torch.device("cpu") | ||||||
|  | # device = torch.device("cuda" if torch.cuda.is_available() else "cpu") | ||||||
| 
 | 
 | ||||||
| # Load tokenizer and model | # load tokenizer and model | ||||||
|  | print("Loading model...") | ||||||
| tokenizer = MobileBertTokenizer.from_pretrained("google/mobilebert-uncased") | tokenizer = MobileBertTokenizer.from_pretrained("google/mobilebert-uncased") | ||||||
| model = MobileBertForSequenceClassification.from_pretrained("cssupport/mobilebert-sql-injection-detect") | model = MobileBertForSequenceClassification.from_pretrained("cssupport/mobilebert-sql-injection-detect") | ||||||
| model.to(device) | model.to(device) | ||||||
| model.eval() | model.eval() | ||||||
|  | print("Model loaded") | ||||||
| 
 | 
 | ||||||
| # Function to predict SQL injection | # function for model to predict sql injection | ||||||
| def predict(text): | def predict(text): | ||||||
|     inputs = tokenizer(text, padding=False, truncation=True, return_tensors="pt", max_length=512) |     inputs = tokenizer(text, padding=False, truncation=True, return_tensors="pt", max_length=512) | ||||||
|     input_ids = inputs["input_ids"].to(device) |     input_ids = inputs["input_ids"].to(device) | ||||||
|  | @ -30,7 +35,7 @@ def predict(text): | ||||||
| 
 | 
 | ||||||
|     return predicted_class, confidence |     return predicted_class, confidence | ||||||
| 
 | 
 | ||||||
| # Define API endpoint | # the api endpoint | ||||||
| @app.route("/predict", methods=["POST"]) | @app.route("/predict", methods=["POST"]) | ||||||
| def classify_query(): | def classify_query(): | ||||||
|     data = request.json |     data = request.json | ||||||
|  | @ -40,16 +45,16 @@ def classify_query(): | ||||||
|     query = data["query"] |     query = data["query"] | ||||||
|     predicted_class, confidence = predict(query) |     predicted_class, confidence = predict(query) | ||||||
| 
 | 
 | ||||||
|     # Thresholding (if confidence > 0.7, mark as SQL Injection) |     # if >0.7, then mark as bad | ||||||
|     is_vulnerable = predicted_class == 1 and confidence > 0.7 |     is_vulnerable = predicted_class == 1 and confidence > 0.7 | ||||||
|     result = { |     result = { | ||||||
|         "query": query, |         "query": query, | ||||||
|         "classification": "SQL Injection Detected" if is_vulnerable else "No SQL Injection Detected", |         "result": "fail" if is_vulnerable else "pass", | ||||||
|         "confidence": round(confidence, 2) |         "confidence": round(confidence, 2) | ||||||
|     } |     } | ||||||
|      |      | ||||||
|     return jsonify(result) |     return jsonify(result) | ||||||
| 
 | 
 | ||||||
| # Run Flask server | # run the flask server | ||||||
| if __name__ == "__main__": | if __name__ == "__main__": | ||||||
|     app.run(host="0.0.0.0", port=5000, debug=True) |     app.run(host="0.0.0.0", port=5000, debug=False) | ||||||
|  |  | ||||||
|  | @ -3,7 +3,7 @@ name = "server-ml" | ||||||
| version = "0.1.0" | version = "0.1.0" | ||||||
| description = "Add your description here" | description = "Add your description here" | ||||||
| readme = "README.md" | readme = "README.md" | ||||||
| requires-python = ">=3.13" | requires-python = ">=3.11" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|     "ctransformers>=0.2.27", |     "ctransformers>=0.2.27", | ||||||
|     "flask>=3.1.0", |     "flask>=3.1.0", | ||||||
|  |  | ||||||
|  | @ -1,3 +1,48 @@ | ||||||
| flask | accelerate==1.3.0 | ||||||
| scikit-learn | blinker==1.9.0 | ||||||
| joblib | certifi==2025.1.31 | ||||||
|  | charset-normalizer==3.4.1 | ||||||
|  | click==8.1.8 | ||||||
|  | ctransformers==0.2.27 | ||||||
|  | filelock==3.17.0 | ||||||
|  | flask==3.1.0 | ||||||
|  | fsspec==2025.2.0 | ||||||
|  | huggingface-hub==0.28.1 | ||||||
|  | idna==3.10 | ||||||
|  | itsdangerous==2.2.0 | ||||||
|  | jinja2==3.1.5 | ||||||
|  | markupsafe==3.0.2 | ||||||
|  | mpmath==1.3.0 | ||||||
|  | networkx==3.4.2 | ||||||
|  | numpy==2.2.2 | ||||||
|  | nvidia-cublas-cu12==12.4.5.8 | ||||||
|  | nvidia-cuda-cupti-cu12==12.4.127 | ||||||
|  | nvidia-cuda-nvrtc-cu12==12.4.127 | ||||||
|  | nvidia-cuda-runtime-cu12==12.4.127 | ||||||
|  | nvidia-cudnn-cu12==9.1.0.70 | ||||||
|  | nvidia-cufft-cu12==11.2.1.3 | ||||||
|  | nvidia-curand-cu12==10.3.5.147 | ||||||
|  | nvidia-cusolver-cu12==11.6.1.9 | ||||||
|  | nvidia-cusparse-cu12==12.3.1.170 | ||||||
|  | nvidia-cusparselt-cu12==0.6.2 | ||||||
|  | nvidia-nccl-cu12==2.21.5 | ||||||
|  | nvidia-nvjitlink-cu12==12.4.127 | ||||||
|  | nvidia-nvtx-cu12==12.4.127 | ||||||
|  | packaging==24.2 | ||||||
|  | peft==0.14.0 | ||||||
|  | psutil==6.1.1 | ||||||
|  | py-cpuinfo==9.0.0 | ||||||
|  | pyyaml==6.0.2 | ||||||
|  | regex==2024.11.6 | ||||||
|  | requests==2.32.3 | ||||||
|  | safetensors==0.5.2 | ||||||
|  | setuptools==75.8.0 | ||||||
|  | sympy==1.13.1 | ||||||
|  | tokenizers==0.21.0 | ||||||
|  | torch==2.6.0 | ||||||
|  | tqdm==4.67.1 | ||||||
|  | transformers==4.48.2 | ||||||
|  | triton==3.2.0 | ||||||
|  | typing-extensions==4.12.2 | ||||||
|  | urllib3==2.3.0 | ||||||
|  | werkzeug==3.1.3 | ||||||
|  |  | ||||||
|  | @ -1,46 +0,0 @@ | ||||||
| # Test Cases |  | ||||||
| 
 |  | ||||||
| ## 1. Basic test cases |  | ||||||
| 
 |  | ||||||
| [Good] `SELECT _ FROM users WHERE id = 1`   |  | ||||||
| [Bad] `SELECT _ FROM users WHERE id = 1 OR 1=1` |  | ||||||
| 
 |  | ||||||
| ## 2. Authentication bypass cases |  | ||||||
| 
 |  | ||||||
| [Bad] `SELECT _ FROM users WHERE username = 'admin' --`   |  | ||||||
| [Bad] `SELECT _ FROM users WHERE username = 'admin' #`   |  | ||||||
| [Bad] `SELECT \* FROM users WHERE username = 'admin' OR '1'='1'` |  | ||||||
| 
 |  | ||||||
| ## 3. Union based injection cases |  | ||||||
| 
 |  | ||||||
| [Bad] `SELECT id, username FROM users WHERE id = 1 UNION SELECT null, 'hacker'`   |  | ||||||
| [Bad] `SELECT id, username FROM users WHERE id = 1 UNION SELECT 1, 'hacked' FROM dual`   |  | ||||||
| [Bad] `SELECT database() UNION SELECT 1` |  | ||||||
| 
 |  | ||||||
| ## 4. Error based injection cases |  | ||||||
| 
 |  | ||||||
| [Bad] `SELECT _ FROM users WHERE id = 1 AND (SELECT 1 FROM users WHERE id=2)=1`   |  | ||||||
| [Bad] `SELECT _ FROM users WHERE id = (SELECT COUNT(\*) FROM users)` |  | ||||||
| 
 |  | ||||||
| ## 5. Blind SQL injection cases |  | ||||||
| 
 |  | ||||||
| [Bad] `SELECT _ FROM users WHERE id = 1; WAITFOR DELAY '00:00:10' --`   |  | ||||||
| [Bad] `SELECT _ FROM users WHERE username = 'admin' AND 1=1` |  | ||||||
| 
 |  | ||||||
| ## 6. Hex and Base64 encoded injection cases |  | ||||||
| 
 |  | ||||||
| [Bad] `SELECT _ FROM users WHERE username = 0x61646D696E`   |  | ||||||
| [Bad] `SELECT _ FROM users WHERE username = 'YWRtaW4='` |  | ||||||
| 
 |  | ||||||
| ## 7. False positives cases |  | ||||||
| 
 |  | ||||||
| [Good] `SELECT _ FROM users WHERE id = 5`   |  | ||||||
| [Good] `SELECT users.name, orders.amount FROM users JOIN orders ON users.id = orders.user_id`   |  | ||||||
| [Good] `SELECT _ FROM users WHERE username = ? AND password = ?` |  | ||||||
| 
 |  | ||||||
| ## 8. Edge cases |  | ||||||
| 
 |  | ||||||
| [Good] `""`   |  | ||||||
| [Bad] `'; --`   |  | ||||||
| [Good] `12345`   |  | ||||||
| [Good] `asdkjhasdkjh` |  | ||||||
|  | @ -1,35 +0,0 @@ | ||||||
| import joblib |  | ||||||
| from sklearn.feature_extraction.text import CountVectorizer |  | ||||||
| from sklearn.linear_model import LogisticRegression |  | ||||||
| from sklearn.pipeline import make_pipeline |  | ||||||
| from sklearn.model_selection import train_test_split |  | ||||||
| 
 |  | ||||||
| # random data |  | ||||||
| data = [ |  | ||||||
|     ("' OR '1'='1", 1), |  | ||||||
|     ("SELECT * FROM users WHERE id=1", 1), |  | ||||||
|     ("DROP TABLE users;", 1), |  | ||||||
|     ("username=admin'--", 1), |  | ||||||
|     ("hello world", 0), |  | ||||||
|     ("this is a normal query", 0), |  | ||||||
|     ("select data from table", 0), |  | ||||||
|     ("just another harmless input", 0), |  | ||||||
| ] |  | ||||||
| 
 |  | ||||||
| queries, labels = zip(*data) |  | ||||||
| 
 |  | ||||||
| # split data into training and testing sets |  | ||||||
| X_train, X_test, y_train, y_test = train_test_split( |  | ||||||
|     queries, labels, test_size=0.2, random_state=42 |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| # build a pipeline with a vectorizer and a logistic regression model |  | ||||||
| pipeline = make_pipeline(CountVectorizer(), LogisticRegression()) |  | ||||||
| 
 |  | ||||||
| # train the model |  | ||||||
| pipeline.fit(X_train, y_train) |  | ||||||
| 
 |  | ||||||
| # save the model to a file |  | ||||||
| joblib.dump(pipeline, "model.pkl") |  | ||||||
| 
 |  | ||||||
| print("Model trained and saved to model.pkl") |  | ||||||
							
								
								
									
										7
									
								
								server/Dockerfile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								server/Dockerfile
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,7 @@ | ||||||
|  | FROM alpine:latest | ||||||
|  | 
 | ||||||
|  | WORKDIR /app | ||||||
|  | 
 | ||||||
|  | COPY server /usr/local/bin/server | ||||||
|  | 
 | ||||||
|  | RUN chmod +x /usr/local/bin/server | ||||||
							
								
								
									
										2
									
								
								server/build.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										2
									
								
								server/build.sh
									
									
									
									
									
										Executable file
									
								
							|  | @ -0,0 +1,2 @@ | ||||||
|  | #!/bin/bash | ||||||
|  | CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o server main.go | ||||||
							
								
								
									
										11
									
								
								server/docker-compose.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								server/docker-compose.yml
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,11 @@ | ||||||
|  | services: | ||||||
|  |   cspj-server: | ||||||
|  |     image: cspj-server | ||||||
|  |     container_name: cspj-server | ||||||
|  |     restart: unless-stopped | ||||||
|  |     network_mode: host | ||||||
|  |     depends_on: | ||||||
|  |       - postgres | ||||||
|  |     volumes: | ||||||
|  |       - "/home/vomitblood/build/cspj-application/docker/chungus/logs/:/tmp" | ||||||
|  |     command: ["server", "-l", "/tmp"] | ||||||
							
								
								
									
										24
									
								
								server/internal/cli/cli.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								server/internal/cli/cli.go
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,24 @@ | ||||||
|  | package cli | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"flag" | ||||||
|  | 	"fmt" | ||||||
|  | 	"os" | ||||||
|  | 
 | ||||||
|  | 	"github.com/Vomitblood/cspj-application/server/internal/config" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func GetFlags() { | ||||||
|  | 	logDir := flag.String("l", "", "Path to the log directory") | ||||||
|  | 
 | ||||||
|  | 	flag.Parse() | ||||||
|  | 
 | ||||||
|  | 	// check if the -l flag is provided
 | ||||||
|  | 	if flag.Lookup("l").Value.String() == "" { | ||||||
|  | 		fmt.Println("Error: The -l flag (log directory) is required.") | ||||||
|  | 		os.Exit(1) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// store the value in a globally accessible config package
 | ||||||
|  | 	config.LogDirectory = *logDir | ||||||
|  | } | ||||||
							
								
								
									
										3
									
								
								server/internal/config/config.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								server/internal/config/config.go
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,3 @@ | ||||||
|  | package config | ||||||
|  | 
 | ||||||
|  | var LogDirectory string | ||||||
|  | @ -87,6 +87,7 @@ func SetupDemoDb(w http.ResponseWriter, r *http.Request) { | ||||||
| 	// avoid duplicate entries and specify roles
 | 	// avoid duplicate entries and specify roles
 | ||||||
| 	insertDataSQL := ` | 	insertDataSQL := ` | ||||||
| 	INSERT INTO users (email, password, role) VALUES | 	INSERT INTO users (email, password, role) VALUES | ||||||
|  | 	('asdf@gmail.com', 'asdf', 'user'), | ||||||
| 	('alice@example.com', 'asdfalicepassword', 'user'), | 	('alice@example.com', 'asdfalicepassword', 'user'), | ||||||
| 	('bob@example.com', 'asdfbobpassword', 'user'), | 	('bob@example.com', 'asdfbobpassword', 'user'), | ||||||
| 	('charlie@example.com', 'asdfcharliepassword', 'admin') | 	('charlie@example.com', 'asdfcharliepassword', 'admin') | ||||||
|  | @ -152,7 +153,6 @@ func FetchEmails() (map[string]bool, error) { | ||||||
| 		emails[email] = true | 		emails[email] = true | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	log.Println("Fetched emails:", emails) |  | ||||||
| 	return emails, nil | 	return emails, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -3,16 +3,17 @@ package log_backup | ||||||
| import ( | import ( | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"log" | 	"log" | ||||||
|  | 	"path/filepath" | ||||||
| 
 | 
 | ||||||
|  | 	"github.com/Vomitblood/cspj-application/server/internal/config" | ||||||
| 	"github.com/Vomitblood/cspj-application/server/internal/webdav" | 	"github.com/Vomitblood/cspj-application/server/internal/webdav" | ||||||
| 	"github.com/studio-b12/gowebdav" | 	"github.com/studio-b12/gowebdav" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| // TODO: use values from config file
 |  | ||||||
| var localLogPaths = []string{ | var localLogPaths = []string{ | ||||||
| 	"/home/vomitblood/build/cspj-application/docker/chungus/logs/host-fs-accesslog.log", | 	filepath.Join(config.LogDirectory, "host-fs-accesslog.log"), | ||||||
| 	"/home/vomitblood/build/cspj-application/docker/chungus/logs/host-fs-auditlog.log", | 	filepath.Join(config.LogDirectory, "host-fs-auditlog.log"), | ||||||
| 	"/home/vomitblood/build/cspj-application/docker/chungus/logs/host-fs-errorlog.log", | 	filepath.Join(config.LogDirectory, "host-fs-errorlog.log"), | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| var remoteFiles = []string{ | var remoteFiles = []string{ | ||||||
|  |  | ||||||
|  | @ -5,17 +5,14 @@ import ( | ||||||
| 	"encoding/json" | 	"encoding/json" | ||||||
| 	"log" | 	"log" | ||||||
| 	"os" | 	"os" | ||||||
|  | 	"path/filepath" | ||||||
| 
 | 
 | ||||||
|  | 	"github.com/Vomitblood/cspj-application/server/internal/config" | ||||||
| 	"github.com/Vomitblood/cspj-application/server/internal/telegram" | 	"github.com/Vomitblood/cspj-application/server/internal/telegram" | ||||||
| 	"github.com/fsnotify/fsnotify" | 	"github.com/fsnotify/fsnotify" | ||||||
| 	tg "github.com/go-telegram-bot-api/telegram-bot-api/v5" | 	tg "github.com/go-telegram-bot-api/telegram-bot-api/v5" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| const ( |  | ||||||
| 	// TODO: add config for user to specify log location
 |  | ||||||
| 	modsecLogFile = "/home/vomitblood/build/cspj-application/docker/chungus/logs/host-fs-auditlog.log" |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| var lastReadPosition int64 = 0 | var lastReadPosition int64 = 0 | ||||||
| 
 | 
 | ||||||
| type LogEntry struct { | type LogEntry struct { | ||||||
|  | @ -25,16 +22,18 @@ type LogEntry struct { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func WatchFile(bot *tg.BotAPI) { | func WatchFile(bot *tg.BotAPI) { | ||||||
|  | 	modsecLogFile := filepath.Join(config.LogDirectory, "host-fs-auditlog.log") | ||||||
|  | 	log.Println("Watching logfile directory:", config.LogDirectory) | ||||||
| 	watcher, err := fsnotify.NewWatcher() | 	watcher, err := fsnotify.NewWatcher() | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		log.Fatal("Failed to initialize watcher:", err) | 		log.Fatal("Failed to initialize watcher: ", err) | ||||||
| 	} | 	} | ||||||
| 	defer watcher.Close() | 	defer watcher.Close() | ||||||
| 
 | 
 | ||||||
| 	// add log file to watcher
 | 	// add log file to watcher
 | ||||||
| 	err = watcher.Add(modsecLogFile) | 	err = watcher.Add(modsecLogFile) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		log.Fatal("Failed to watch log file:", err) | 		log.Fatal("Failed to watch log file: ", err) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	log.Println("Monitoring log file for changes...") | 	log.Println("Monitoring log file for changes...") | ||||||
|  | @ -54,15 +53,16 @@ func WatchFile(bot *tg.BotAPI) { | ||||||
| 			if !ok { | 			if !ok { | ||||||
| 				return | 				return | ||||||
| 			} | 			} | ||||||
| 			log.Println("Watcher error:", err) | 			log.Println("Watcher error: ", err) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func readNewLines(bot *tg.BotAPI) { | func readNewLines(bot *tg.BotAPI) { | ||||||
|  | 	modsecLogFile := filepath.Join(config.LogDirectory, "host-fs-auditlog.log") | ||||||
| 	file, err := os.Open(modsecLogFile) | 	file, err := os.Open(modsecLogFile) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		log.Println("Failed to reopen log file:", err) | 		log.Println("Failed to reopen log file: ", err) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 	defer file.Close() | 	defer file.Close() | ||||||
|  | @ -77,7 +77,7 @@ func readNewLines(bot *tg.BotAPI) { | ||||||
| 
 | 
 | ||||||
| 		// try to parse json
 | 		// try to parse json
 | ||||||
| 		if err := json.Unmarshal([]byte(line), &logEntry); err != nil { | 		if err := json.Unmarshal([]byte(line), &logEntry); err != nil { | ||||||
| 			log.Println("⚠️ Failed to parse JSON:", err) | 			log.Println("⚠️ Failed to parse JSON: ", err) | ||||||
| 			// skip invalid json lines
 | 			// skip invalid json lines
 | ||||||
| 			// very crucial as modsecurity does not respect the json spec
 | 			// very crucial as modsecurity does not respect the json spec
 | ||||||
| 			continue | 			continue | ||||||
|  | @ -93,6 +93,6 @@ func readNewLines(bot *tg.BotAPI) { | ||||||
| 	lastReadPosition, _ = file.Seek(0, os.SEEK_CUR) | 	lastReadPosition, _ = file.Seek(0, os.SEEK_CUR) | ||||||
| 
 | 
 | ||||||
| 	if err := scanner.Err(); err != nil { | 	if err := scanner.Err(); err != nil { | ||||||
| 		log.Println("Error reading log file:", err) | 		log.Println("Error reading log file: ", err) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,6 +1,7 @@ | ||||||
| package sql_injection | package sql_injection | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
|  | 	"bytes" | ||||||
| 	"context" | 	"context" | ||||||
| 	"encoding/json" | 	"encoding/json" | ||||||
| 	"fmt" | 	"fmt" | ||||||
|  | @ -134,6 +135,7 @@ func SecureRegisterSql(w http.ResponseWriter, r *http.Request) { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func UnsecureLoginSql(w http.ResponseWriter, r *http.Request) { | func UnsecureLoginSql(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 	log.Println("/unsecure-login-sql hit") | ||||||
| 	// decode the json body
 | 	// decode the json body
 | ||||||
| 	var requestData struct { | 	var requestData struct { | ||||||
| 		Email    string `json:"email"` | 		Email    string `json:"email"` | ||||||
|  | @ -175,12 +177,18 @@ func UnsecureLoginSql(w http.ResponseWriter, r *http.Request) { | ||||||
| 
 | 
 | ||||||
| // very secure login endpoint
 | // very secure login endpoint
 | ||||||
| func SecureLoginSql(w http.ResponseWriter, r *http.Request) { | func SecureLoginSql(w http.ResponseWriter, r *http.Request) { | ||||||
| 	// decode the json body
 | 	log.Println("/secure-login-sql hit") | ||||||
| 	var requestData struct { | 	var requestData struct { | ||||||
| 		Email    string `json:"email"` | 		Email    string `json:"email"` | ||||||
| 		Password string `json:"password"` | 		Password string `json:"password"` | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	type AIResponse struct { | ||||||
|  | 		Confidence float64 `json:"confidence"` | ||||||
|  | 		Query      string  `json:"query"` | ||||||
|  | 		Result     string  `json:"result"` | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	// declare new json decoder with custom property
 | 	// declare new json decoder with custom property
 | ||||||
| 	jsonDecoder := json.NewDecoder(r.Body) | 	jsonDecoder := json.NewDecoder(r.Body) | ||||||
| 	// rejects any unknown fields in the json, more strict
 | 	// rejects any unknown fields in the json, more strict
 | ||||||
|  | @ -214,6 +222,35 @@ func SecureLoginSql(w http.ResponseWriter, r *http.Request) { | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	// make insecure sql command just to send to ai model to check
 | ||||||
|  | 	insecureQuery := fmt.Sprintf("SELECT id, email, password FROM users WHERE email = '%s' AND password = '%s'", requestData.Email, requestData.Password) | ||||||
|  | 
 | ||||||
|  | 	// send the query to the ai model server
 | ||||||
|  | 	aiReqBody, _ := json.Marshal(map[string]string{"query": insecureQuery}) | ||||||
|  | 	aiResp, err := http.Post("http://127.0.0.1:5000/predict", "application/json", bytes.NewBuffer(aiReqBody)) | ||||||
|  | 	if err != nil { | ||||||
|  | 		http.Error(w, "Internal Server Error", http.StatusInternalServerError) | ||||||
|  | 		log.Println("Failed to reach AI model:", err) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	defer aiResp.Body.Close() | ||||||
|  | 
 | ||||||
|  | 	var aiResponse AIResponse | ||||||
|  | 	if err := json.NewDecoder(aiResp.Body).Decode(&aiResponse); err != nil { | ||||||
|  | 		http.Error(w, "Failed to decode AI response", http.StatusInternalServerError) | ||||||
|  | 		log.Println("Error decoding AI response:", err) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	log.Println("AI Response:", aiResponse.Result) | ||||||
|  | 
 | ||||||
|  | 	// reject the original request if ai result is fail
 | ||||||
|  | 	if aiResponse.Result == "fail" { | ||||||
|  | 		http.Error(w, "Potential SQL injection detected", http.StatusForbidden) | ||||||
|  | 		log.Println("AI detected SQL injection attempt") | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	// retrieve the email and password from the database
 | 	// retrieve the email and password from the database
 | ||||||
| 	var id int | 	var id int | ||||||
| 	var email string | 	var email string | ||||||
|  |  | ||||||
|  | @ -3,6 +3,8 @@ package main | ||||||
| import ( | import ( | ||||||
| 	"log" | 	"log" | ||||||
| 
 | 
 | ||||||
|  | 	"github.com/Vomitblood/cspj-application/server/internal/cli" | ||||||
|  | 	"github.com/Vomitblood/cspj-application/server/internal/config" | ||||||
| 	"github.com/Vomitblood/cspj-application/server/internal/db" | 	"github.com/Vomitblood/cspj-application/server/internal/db" | ||||||
| 	"github.com/Vomitblood/cspj-application/server/internal/http_server" | 	"github.com/Vomitblood/cspj-application/server/internal/http_server" | ||||||
| 	"github.com/Vomitblood/cspj-application/server/internal/log_watcher" | 	"github.com/Vomitblood/cspj-application/server/internal/log_watcher" | ||||||
|  | @ -11,6 +13,9 @@ import ( | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| func main() { | func main() { | ||||||
|  | 	cli.GetFlags() | ||||||
|  | 	log.Printf("Log directory set to: %s", config.LogDirectory) | ||||||
|  | 
 | ||||||
| 	var err error | 	var err error | ||||||
| 	db.DbPool, err = db.ConnectToDb() | 	db.DbPool, err = db.ConnectToDb() | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
|  | @ -27,7 +32,5 @@ func main() { | ||||||
| 	// start log watcher
 | 	// start log watcher
 | ||||||
| 	go log_watcher.WatchFile(tgBot) | 	go log_watcher.WatchFile(tgBot) | ||||||
| 
 | 
 | ||||||
| 	//
 |  | ||||||
| 
 |  | ||||||
| 	http_server.ServeApi() | 	http_server.ServeApi() | ||||||
| } | } | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue