PCRE2 standard applied

This commit is contained in:
DomySh
2022-07-01 02:29:28 +02:00
parent 8d41b94c36
commit 509fdbc8ca
14 changed files with 66 additions and 155 deletions

View File

@@ -1,7 +1,13 @@
#Building main conteiner #Building main conteiner
FROM python:slim-buster FROM python:slim-buster
RUN apt-get update && apt-get -y install build-essential libboost-system-dev libboost-thread-dev RUN apt-get update && apt-get -y install build-essential libboost-system-dev libboost-thread-dev libpcre2-dev git
WORKDIR /tmp/
RUN git clone --branch release https://github.com/jpcre2/jpcre2
WORKDIR /tmp/jpcre2
RUN ./configure; make; make install
WORKDIR /
RUN mkdir /execute RUN mkdir /execute
WORKDIR /execute WORKDIR /execute
@@ -12,7 +18,7 @@ RUN pip install --no-cache-dir -r /execute/requirements.txt
ARG GCC_PARAMS ARG GCC_PARAMS
RUN mkdir proxy RUN mkdir proxy
ADD ./backend/proxy/proxy.cpp /execute/proxy/proxy.cpp ADD ./backend/proxy/proxy.cpp /execute/proxy/proxy.cpp
RUN c++ -O3 -march=native $GCC_PARAMS -o proxy/proxy proxy/proxy.cpp -pthread -lboost_system -lboost_thread RUN c++ -O3 -march=native $GCC_PARAMS -o proxy/proxy proxy/proxy.cpp -pthread -lboost_system -lboost_thread -lpcre2-8
COPY ./backend/ /execute/ COPY ./backend/ /execute/
COPY ./frontend/build/ ./frontend/ COPY ./frontend/build/ ./frontend/

View File

@@ -269,6 +269,7 @@ class RegexAddForm(BaseModel):
service_id: str service_id: str
regex: str regex: str
mode: str mode: str
active: Union[bool,None]
is_blacklist: bool is_blacklist: bool
is_case_sensitive: bool is_case_sensitive: bool
@@ -279,8 +280,8 @@ async def post_regexes_add(form: RegexAddForm, auth: bool = Depends(is_loggined)
except Exception: except Exception:
return {"status":"Invalid regex"} return {"status":"Invalid regex"}
try: try:
db.query("INSERT INTO regexes (service_id, regex, is_blacklist, mode, is_case_sensitive ) VALUES (?, ?, ?, ?, ?);", db.query("INSERT INTO regexes (service_id, regex, is_blacklist, mode, is_case_sensitive, active ) VALUES (?, ?, ?, ?, ?, ?);",
form.service_id, form.regex, form.is_blacklist, form.mode, form.is_case_sensitive) form.service_id, form.regex, form.is_blacklist, form.mode, form.is_case_sensitive, True if form.active is None else form.active )
except sqlite3.IntegrityError: except sqlite3.IntegrityError:
return {'status': 'An identical regex already exists'} return {'status': 'An identical regex already exists'}

View File

@@ -7,7 +7,6 @@
#include <cstddef> #include <cstddef>
#include <iostream> #include <iostream>
#include <string> #include <string>
#include <regex>
#include <mutex> #include <mutex>
#include <boost/thread.hpp> #include <boost/thread.hpp>
@@ -16,7 +15,9 @@
#include <boost/bind/bind.hpp> #include <boost/bind/bind.hpp>
#include <boost/asio.hpp> #include <boost/asio.hpp>
#include <boost/thread/mutex.hpp> #include <boost/thread/mutex.hpp>
#include <jpcre2.hpp>
typedef jpcre2::select<char> jp;
using namespace std; using namespace std;
bool unhexlify(string const &hex, string &newString) { bool unhexlify(string const &hex, string &newString) {
@@ -35,7 +36,8 @@ bool unhexlify(string const &hex, string &newString) {
} }
} }
typedef vector<pair<string,regex>> regex_rule_vector; typedef pair<string,jp::Regex> regex_rule_pair;
typedef vector<regex_rule_pair> regex_rule_vector;
struct regex_rules{ struct regex_rules{
regex_rule_vector regex_s_c_w, regex_c_s_w, regex_s_c_b, regex_c_s_b; regex_rule_vector regex_s_c_w, regex_c_s_w, regex_s_c_b, regex_c_s_b;
@@ -62,24 +64,14 @@ struct regex_rules{
if (arg[1] != 'C' && arg[1] != 'c' && arg[1] != 'S' && arg[1] != 's') return; if (arg[1] != 'C' && arg[1] != 'c' && arg[1] != 'S' && arg[1] != 's') return;
string hex(arg+2), expr; string hex(arg+2), expr;
if (!unhexlify(hex, expr)) return; if (!unhexlify(hex, expr)) return;
//Push regex
try{ jp::Regex regex(expr,arg[0] == '1'?"gS":"giS");
if (regex){
//Push regex #ifdef DEBUG
if (arg[0] == '1'){ cerr << "Added regex " << expr << " " << arg << endl;
regex regex(expr); #endif
#ifdef DEBUG getByCode(arg[1])->push_back(make_pair(string(arg), regex));
cerr << "Added case sensitive regex " << expr << endl; } else {
#endif
getByCode(arg[1])->push_back(make_pair(string(arg), regex));
} else {
regex regex(expr,regex_constants::icase);
#ifdef DEBUG
cerr << "Added case insensitive regex " << expr << endl;
#endif
getByCode(arg[1])->push_back(make_pair(string(arg), regex));
}
} catch(...){
cerr << "Regex " << arg << " was not compiled successfully" << endl; cerr << "Regex " << arg << " was not compiled successfully" << endl;
} }
} }
@@ -92,17 +84,19 @@ mutex update_mutex;
mutex stdout_mutex; mutex stdout_mutex;
#endif #endif
bool filter_data(unsigned char* data, const size_t& bytes_transferred, vector<pair<string,regex>> const &blacklist, vector<pair<string,regex>> const &whitelist){ bool filter_data(unsigned char* data, const size_t& bytes_transferred, regex_rule_vector const &blacklist, regex_rule_vector const &whitelist){
#ifdef DEBUG_PACKET #ifdef DEBUG_PACKET
cerr << "---------------- Packet ----------------" << endl; cerr << "---------------- Packet ----------------" << endl;
for(int i=0;i<bytes_transferred;i++){ for(int i=0;i<bytes_transferred;i++) cerr << data[i];
cerr << data[i]; cerr << endl;
} for(int i=0;i<bytes_transferred;i++) fprintf(stderr, "%x", data[i]);
cerr << "\n" << "---------------- End Packet ----------------" << endl; cerr << endl;
cerr << "---------------- End Packet ----------------" << endl;
#endif #endif
for (pair<string,regex> ele:blacklist){ string str_data((char *) data, bytes_transferred);
for (regex_rule_pair ele:blacklist){
try{ try{
if(regex_search(reinterpret_cast<const char*>(data), reinterpret_cast<const char*>(data)+bytes_transferred, ele.second)){ if(ele.second.match(str_data)){
#ifdef MULTI_THREAD #ifdef MULTI_THREAD
std::unique_lock<std::mutex> lck(stdout_mutex); std::unique_lock<std::mutex> lck(stdout_mutex);
#endif #endif
@@ -113,9 +107,9 @@ bool filter_data(unsigned char* data, const size_t& bytes_transferred, vector<pa
cerr << "Error while matching regex: " << ele.first << endl; cerr << "Error while matching regex: " << ele.first << endl;
} }
} }
for (pair<string,regex> ele:whitelist){ for (regex_rule_pair ele:whitelist){
try{ try{
if(!regex_search(reinterpret_cast<const char*>(data),reinterpret_cast<const char*>(data)+bytes_transferred, ele.second)){ if(!ele.second.match(str_data)){
#ifdef MULTI_THREAD #ifdef MULTI_THREAD
std::unique_lock<std::mutex> lck(stdout_mutex); std::unique_lock<std::mutex> lck(stdout_mutex);
#endif #endif
@@ -394,8 +388,9 @@ void update_config (boost::asio::streambuf &input_buffer){
std::unique_lock<std::mutex> lck(update_mutex); std::unique_lock<std::mutex> lck(update_mutex);
regex_rules *regex_new_config = new regex_rules(); regex_rules *regex_new_config = new regex_rules();
string data; string data;
while(!config_stream.eof()){ while(true){
config_stream >> data; config_stream >> data;
if (config_stream.eof()) break;
regex_new_config->add(data.c_str()); regex_new_config->add(data.c_str());
} }
regex_config.reset(regex_new_config); regex_config.reset(regex_new_config);

View File

@@ -1,46 +0,0 @@
#Frontend build
FROM node:lts-alpine AS frontend
RUN apk add --update npm
RUN mkdir /app
WORKDIR /app
ENV PATH /app/node_modules/.bin:$PATH
ADD ./frontend/package.json .
ADD ./frontend/package-lock.json .
RUN npm ci --silent
COPY ./frontend/ .
RUN npm run build
#Building main conteiner
FROM python:slim-buster
RUN apt-get update && apt-get -y install curl supervisor gettext-base build-essential libboost-dev nginx libboost-regex-dev libboost-system-dev
RUN curl -sL https://deb.nodesource.com/setup_16.x | bash
RUN apt-get install nodejs
RUN npm install serve -g --silent
RUN mkdir /execute
WORKDIR /execute
ADD ./backend/requirements.txt /execute/requirements.txt
RUN pip install --no-cache-dir -r /execute/requirements.txt
COPY ./backend/ /execute/
RUN c++ -O3 -o proxy/proxy proxy/proxy.cpp -pthread -lboost_system
COPY ./config/supervisord.conf /etc/supervisor/supervisord.conf
COPY ./config/nginx.conf /tmp/nginx.conf
COPY ./config/start_nginx.sh /tmp/start_nginx.sh
#Copy react app in the main container
COPY --from=frontend /app/build/ ./frontend/
RUN usermod -a -G root nobody
RUN chown -R nobody:root /execute && \
chmod -R 660 /execute && chmod -R u+X /execute
RUN chmod ug+x /execute/proxy/proxy
ENTRYPOINT ["/usr/bin/supervisord","-c","/etc/supervisor/supervisord.conf"]

View File

@@ -1,13 +1,13 @@
{ {
"files": { "files": {
"main.css": "/static/css/main.c375ae17.css", "main.css": "/static/css/main.c375ae17.css",
"main.js": "/static/js/main.bf062beb.js", "main.js": "/static/js/main.b694f5e6.js",
"index.html": "/index.html", "index.html": "/index.html",
"main.c375ae17.css.map": "/static/css/main.c375ae17.css.map", "main.c375ae17.css.map": "/static/css/main.c375ae17.css.map",
"main.bf062beb.js.map": "/static/js/main.bf062beb.js.map" "main.b694f5e6.js.map": "/static/js/main.b694f5e6.js.map"
}, },
"entrypoints": [ "entrypoints": [
"static/css/main.c375ae17.css", "static/css/main.c375ae17.css",
"static/js/main.bf062beb.js" "static/js/main.b694f5e6.js"
] ]
} }

View File

@@ -1 +1 @@
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/favicon.ico"/><link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png"><link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png"><link rel="manifest" href="/site.webmanifest"><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#FFFFFFFF"/><meta name="description" content="Firegex by Pwnzer0tt1"/><title>Firegex</title><script defer="defer" src="/static/js/main.bf062beb.js"></script><link href="/static/css/main.c375ae17.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html> <!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/favicon.ico"/><link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png"><link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png"><link rel="manifest" href="/site.webmanifest"><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#FFFFFFFF"/><meta name="description" content="Firegex by Pwnzer0tt1"/><title>Firegex</title><script defer="defer" src="/static/js/main.b694f5e6.js"></script><link href="/static/css/main.c375ae17.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>

File diff suppressed because one or more lines are too long

View File

@@ -1,8 +1,8 @@
import { Button, Group, Space, TextInput, Notification, Switch, NativeSelect, Tooltip, Modal } from '@mantine/core'; import { Button, Group, Space, TextInput, Notification, Switch, NativeSelect, Modal } from '@mantine/core';
import { useForm } from '@mantine/hooks'; import { useForm } from '@mantine/hooks';
import React, { useState } from 'react'; import React, { useState } from 'react';
import { RegexAddForm } from '../js/models'; import { RegexAddForm } from '../js/models';
import { addregex, b64encode, fireUpdateRequest, getBinaryRegex, getHumanReadableRegex, okNotify } from '../js/utils'; import { addregex, b64decode, b64encode, fireUpdateRequest, okNotify } from '../js/utils';
import { ImCross } from "react-icons/im" import { ImCross } from "react-icons/im"
import FilterTypeSelector from './FilterTypeSelector'; import FilterTypeSelector from './FilterTypeSelector';
@@ -12,7 +12,7 @@ type RegexAddInfo = {
type:string, type:string,
mode:string, mode:string,
is_case_insensitive:boolean, is_case_insensitive:boolean,
percentage_encoding:boolean deactive:boolean
} }
function AddNewRegex({ opened, onClose, service }:{ opened:boolean, onClose:()=>void, service:string }) { function AddNewRegex({ opened, onClose, service }:{ opened:boolean, onClose:()=>void, service:string }) {
@@ -23,7 +23,7 @@ function AddNewRegex({ opened, onClose, service }:{ opened:boolean, onClose:()=>
type:"blacklist", type:"blacklist",
mode:"C -> S", mode:"C -> S",
is_case_insensitive:false, is_case_insensitive:false,
percentage_encoding:false deactive:false
}, },
validationRules:{ validationRules:{
regex: (value) => value !== "", regex: (value) => value !== "",
@@ -45,17 +45,13 @@ function AddNewRegex({ opened, onClose, service }:{ opened:boolean, onClose:()=>
setSubmitLoading(true) setSubmitLoading(true)
const filter_mode = ({'C -> S':'C', 'S -> C':'S', 'C <-> S':'B'}[values.mode]) const filter_mode = ({'C -> S':'C', 'S -> C':'S', 'C <-> S':'B'}[values.mode])
let final_regex:string|number[] = values.regex
if (values.percentage_encoding){
final_regex = getBinaryRegex(final_regex)
}
const request:RegexAddForm = { const request:RegexAddForm = {
is_blacklist:values.type !== "whitelist", is_blacklist:values.type !== "whitelist",
is_case_sensitive: !values.is_case_insensitive, is_case_sensitive: !values.is_case_insensitive,
service_id: service, service_id: service,
mode: filter_mode?filter_mode:"B", mode: filter_mode?filter_mode:"B",
regex: b64encode(final_regex) regex: b64encode(values.regex),
active: !values.deactive
} }
setSubmitLoading(false) setSubmitLoading(false)
addregex(request).then( res => { addregex(request).then( res => {
@@ -63,7 +59,7 @@ function AddNewRegex({ opened, onClose, service }:{ opened:boolean, onClose:()=>
setSubmitLoading(false) setSubmitLoading(false)
close(); close();
fireUpdateRequest(); fireUpdateRequest();
okNotify(`Regex ${getHumanReadableRegex(request.regex)} has been added`, `Successfully added ${request.is_case_sensitive?"case sensitive":"case insensitive"} ${request.is_blacklist?"blacklist":"whitelist"} regex to ${request.service_id} service`) okNotify(`Regex ${b64decode(request.regex)} has been added`, `Successfully added ${request.is_case_sensitive?"case sensitive":"case insensitive"} ${request.is_blacklist?"blacklist":"whitelist"} regex to ${request.service_id} service`)
}else if (res.toLowerCase() === "invalid regex"){ }else if (res.toLowerCase() === "invalid regex"){
setSubmitLoading(false) setSubmitLoading(false)
form.setFieldError("regex", "Invalid Regex") form.setFieldError("regex", "Invalid Regex")
@@ -87,19 +83,16 @@ function AddNewRegex({ opened, onClose, service }:{ opened:boolean, onClose:()=>
{...form.getInputProps('regex')} {...form.getInputProps('regex')}
/> />
<Space h="md" /> <Space h="md" />
<Tooltip label="To represent binary data use URL encoding. Example: %01" transition="slide-right" openDelay={500} transitionDuration={500} transitionTimingFunction="ease"
color="gray" wrapLines width={220} withArrow position='right' gutter={20}>
<Switch
label="Use percentage encoding for binary values"
{...form.getInputProps('percentage_encoding', { type: 'checkbox' })}
/>
</Tooltip>
<Space h="md" />
<Switch <Switch
label="Case insensitive" label="Case insensitive"
{...form.getInputProps('is_case_insensitive', { type: 'checkbox' })} {...form.getInputProps('is_case_insensitive', { type: 'checkbox' })}
/> />
<Space h="md" /> <Space h="md" />
<Switch
label="Deactivate"
{...form.getInputProps('deactive', { type: 'checkbox' })}
/>
<Space h="md" />
<NativeSelect <NativeSelect
data={['C -> S', 'S -> C', 'C <-> S']} data={['C -> S', 'S -> C', 'C <-> S']}
label="Choose the source of the packets to filter" label="Choose the source of the packets to filter"

View File

@@ -1,7 +1,7 @@
import { Grid, Text, Title, Badge, Space, ActionIcon, Tooltip } from '@mantine/core'; import { Grid, Text, Title, Badge, Space, ActionIcon, Tooltip } from '@mantine/core';
import React, { useState } from 'react'; import React, { useState } from 'react';
import { RegexFilter } from '../../js/models'; import { RegexFilter } from '../../js/models';
import { activateregex, deactivateregex, deleteregex, errorNotify, fireUpdateRequest, getHumanReadableRegex, okNotify } from '../../js/utils'; import { activateregex, b64decode, deactivateregex, deleteregex, errorNotify, fireUpdateRequest, okNotify } from '../../js/utils';
import style from "./RegexView.module.scss"; import style from "./RegexView.module.scss";
import { BsTrashFill } from "react-icons/bs" import { BsTrashFill } from "react-icons/bs"
import YesNoModal from '../YesNoModal'; import YesNoModal from '../YesNoModal';
@@ -15,7 +15,7 @@ function RegexView({ regexInfo }:{ regexInfo:RegexFilter }) {
regexInfo.mode === "S"? "S -> C": regexInfo.mode === "S"? "S -> C":
regexInfo.mode === "B"? "S <-> C": "🤔" regexInfo.mode === "B"? "S <-> C": "🤔"
let regex_expr = getHumanReadableRegex(regexInfo.regex); let regex_expr = b64decode(regexInfo.regex);
const [deleteModal, setDeleteModal] = useState(false); const [deleteModal, setDeleteModal] = useState(false);
const [deleteTooltipOpened, setDeleteTooltipOpened] = useState(false); const [deleteTooltipOpened, setDeleteTooltipOpened] = useState(false);

View File

@@ -4,7 +4,7 @@ import React, { useEffect, useState } from 'react';
import { changeports, fireUpdateRequest, okNotify } from '../../js/utils'; import { changeports, fireUpdateRequest, okNotify } from '../../js/utils';
import { ImCross } from "react-icons/im" import { ImCross } from "react-icons/im"
import { Service } from '../../js/models'; import { Service } from '../../js/models';
import { BsArrowDownSquareFill } from 'react-icons/bs'; import { FaLongArrowAltDown } from 'react-icons/fa';
type InputForm = { type InputForm = {
internalPort:number, internalPort:number,
@@ -70,7 +70,7 @@ function ChangePortModal({ service, opened, onClose }:{ service:Service, opened:
/> />
<Space h="xl" /> <Space h="xl" />
<Center><BsArrowDownSquareFill size={50}/></Center> <Center><FaLongArrowAltDown size={50}/></Center>
<NumberInput <NumberInput
placeholder="8080" placeholder="8080"

View File

@@ -74,5 +74,6 @@ export type RegexAddForm = {
regex:string, regex:string,
is_case_sensitive:boolean, is_case_sensitive:boolean,
is_blacklist:boolean, is_blacklist:boolean,
mode:string // C->S S->C BOTH mode:string, // C->S S->C BOTH,
active: boolean
} }

View File

@@ -152,50 +152,7 @@ export async function serviceregexlist(service_id:string){
return await getapi(`service/${service_id}/regexes`) as RegexFilter[]; return await getapi(`service/${service_id}/regexes`) as RegexFilter[];
} }
const unescapedChars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!\"#$&'()*+,-./:;<=>?@[\\]^_`{|}~ ";
export function getHumanReadableRegex(regexB64:string){
const regex = Buffer.from(regexB64, "base64")
let res = ""
for (let i=0; i < regex.length; i++){
const byte = String.fromCharCode(regex[i]);
if (unescapedChars.includes(byte)){
res+=byte
}else{
let hex_data = regex[i].toString(16)
if (hex_data.length === 1) hex_data = "0"+hex_data
res+="%"+hex_data
}
}
return res
}
const hexChars = "0123456789abcdefABCDEF"
export function getBinaryRegex(regexPercentageEncoded:string):number[]{
const regex = Buffer.from(regexPercentageEncoded)
let res = []
for (let i=0; i < regex.length; i++){
const byte = String.fromCharCode(regex[i]);
if ("%" === byte){
if(i+2 < regex.length){
const byte_1 = String.fromCharCode(regex[i+1]);
const byte_2 = String.fromCharCode(regex[i+2]);
if(hexChars.includes(byte_1) && hexChars.includes(byte_2)){
res.push(parseInt(byte_1+byte_2,16))
i += 2
}else{
res.push(regex[i])
}
}else{
res.push(regex[i])
}
}else{
res.push(regex[i])
}
}
return res
}
export function errorNotify(title:string, description:string ){ export function errorNotify(title:string, description:string ){
showNotification({ showNotification({
@@ -220,3 +177,7 @@ export function okNotify(title:string, description:string ){
export function b64encode(data:number[]|string){ export function b64encode(data:number[]|string){
return Buffer.from(data).toString('base64') return Buffer.from(data).toString('base64')
} }
export function b64decode(regexB64:string){
return Buffer.from(regexB64, "base64").toString()
}