Team Ai
Apppublic

Rotaba/structured-data-anonymizer

sourceHugging Facemitupdated 4y agoView on Hugging Face
0likes
app.py502 linesDownload Raw Back to root
1 2"""Streamlit app for Presidio + Privy-trained PII models."""3 4import spacy5import en_spacy_pii_distilbert6from spacy_recognizer import CustomSpacyRecognizer7from presidio_analyzer.nlp_engine import NlpEngineProvider8from presidio_anonymizer import AnonymizerEngine9from presidio_analyzer import AnalyzerEngine, RecognizerRegistry10import pandas as pd11from annotated_text import annotated_text12from json import JSONEncoder13import json14import warnings15import streamlit as st16# from streamlit import logger as _logger17import os18import csv19import json20from chatgpt_wrapper import ChatGPT21import time22 23os.environ["TOKENIZERS_PARALLELISM"] = "false"24warnings.filterwarnings('ignore')25 26# from flair_recognizer import FlairRecognizer27 28def load_data(file_location):29    unpacked_string_data = []30    unpacked_url_data = []31    unpacked_json_data = []32    # Read the data back from the CSV file and unpack it33    with open(file_location, mode='r') as csv_file:34        reader = csv.reader(csv_file)35        for row in reader:36            unpacked_string_data.append(row[0])37            unpacked_url_data.append(row[1])38            unpacked_json_data.append(json.loads(row[2]))39            # print("Unpacked string data:", unpacked_string_data)40            # print("Unpacked url data:", unpacked_url_data)41            # print("Unpacked JSON data:", unpacked_json_data)42    return unpacked_string_data, dict(zip(unpacked_string_data, unpacked_json_data))43 44# Helper methods45@st.cache_resource #(allow_output_mutation=True)46def analyzer_engine(use_local=None):47    """Return AnalyzerEngine."""48 49    spacy_recognizer = CustomSpacyRecognizer()50    if use_local:51        # !pip52        # install53        # https: // huggingface.co / beki / en_spacy_pii_distilbert / resolve / main / en_spacy_pii_distilbert - any - py3 - none - any.whl54 55        # Using spacy.load().56        nlp = spacy.load("en_spacy_pii_distilbert")57 58        # Importing as module.59        nlp_engine = en_spacy_pii_distilbert.load()60    else:61        configuration = {62            # print("ENALBEE MODELES")63            "nlp_engine_name": "spacy",64            "models": [65                {"lang_code": "en", "model_name": "en_spacy_pii_distilbert"}],66        }67 68        # Create NLP engine based on configuration69        provider = NlpEngineProvider(nlp_configuration=configuration)70        nlp_engine = provider.create_engine()71 72    registry = RecognizerRegistry()73    # add rule-based recognizers74    registry.load_predefined_recognizers(nlp_engine=nlp_engine)75    registry.add_recognizer(spacy_recognizer)76    # remove the nlp engine we passed, to use custom label mappings77    registry.remove_recognizer("SpacyRecognizer")78 79    analyzer = AnalyzerEngine(nlp_engine=nlp_engine,80                              registry=registry, supported_languages=["en"])81 82    # uncomment for flair-based NLP recognizer83    # flair_recognizer = FlairRecognizer()84    # registry.load_predefined_recognizers()85    # registry.add_recognizer(flair_recognizer)86    # analyzer = AnalyzerEngine(registry=registry, supported_languages=["en"])87    return analyzer88 89 90@st.cache_resource#(allow_output_mutation=True)91def anonymizer_engine():92    """Return AnonymizerEngine."""93    return AnonymizerEngine()94 95 96def get_supported_entities():97    """Return supported entities from the Analyzer Engine."""98    return analyzer_engine().get_supported_entities()99 100 101def analyze(**kwargs):102    """Analyze input using Analyzer engine and input arguments (kwargs)."""103    if "entities" not in kwargs or "All" in kwargs["entities"]:104        kwargs["entities"] = None105    return analyzer_engine().analyze(**kwargs)106 107 108def anonymize(text, analyze_results):109    """Anonymize identified input using Presidio Abonymizer."""110    if not text:111        return112    res = anonymizer_engine().anonymize(text, analyze_results)113    return res.text114 115 116def annotate(text, st_analyze_results, st_entities):117    tokens = []118    # sort by start index119    results = sorted(st_analyze_results, key=lambda x: x.start)120    for i, res in enumerate(results):121        if i == 0:122            tokens.append(text[:res.start])123 124        # append entity text and entity type125        tokens.append((text[res.start: res.end], res.entity_type))126 127        # if another entity coming i.e. we're not at the last results element, add text up to next entity128        if i != len(results) - 1:129            tokens.append(text[res.end:results[i+1].start])130        # if no more entities coming, add all remaining text131        else:132            tokens.append(text[res.end:])133    return tokens134 135 136st.set_page_config(page_title="Bitahoy demo", layout="wide")137 138# Side bar -------------------------------------------139# add picture with140st.sidebar.image("structured-data-anonymizer/assets/bitahoy-logo.png", width=200)141 142st.sidebar.markdown(143"""Detect and anonymize PII in structured text such as protocol traces (JSON, SQL, XML etc.)"""144)145 146# dropdown147# titles, json_dict = load_data("structured-data-anonymizer/assets/data_s_short.csv")148# option_list = titles149# option = st.sidebar.selectbox(150#     'Choose an existing structured input?',151#     option_list)152 153# dropdown df154# Title,Url,Dict,Prompt,Result155dataframe = pd.read_csv("structured-data-anonymizer/assets/df_data_short.csv")156# select only the third column of the data frame157# select only first column of the data frame158titles = dataframe['Title']159# conver it to a list160titles = titles.values.tolist()161# print(dataframe.iloc[0])162# select first row from dataframe163option_list = titles164# for i in option_list:165#     if (dataframe[dataframe['Title'] == i]['Result'].empty):166#         i = i + "*"167# print(option_list)168option = st.sidebar.selectbox(169    'Choose an existing structured input?',170    option_list)171 172# # st.sidebar.write('You selected:', option)173# json_dict = dataframe['Dict']174# json_dict = json_dict.values.tolist()175sidebar_text = 'Use small icon-button in right corner to copy input to clipboard'176st.sidebar.write(sidebar_text)177json_dict_option = dataframe[dataframe['Title'] == option]['Dict'].values[0]178 179st.sidebar.code (json_dict_option)180 181#romans complex dropdown182# st.checkbox("Enable/Disable input of existing data", key="disabled")183#184#     option = st.selectbox(185#         "Choose an existing structured input?",186#         option_list,187#         # label_visibility=st.session_state.visibility,188#         disabled=st.session_state.disabled,189#     )190#     st.write('You selected:', option)191 192st_entities = st.sidebar.multiselect(193    label="Which entities to look for?",194    options=get_supported_entities(),195    default=['PHONE_NUMBER', 'CREDIT_CARD', 'DATE_TIME', 'MEDICAL_LICENSE', 'US_BANK_NUMBER', 'IP_ADDRESS', 'IBAN_CODE', 'LOCATION', 'EMAIL_ADDRESS']196#     default=list(get_supported_entities()),197)198# ['PHONE_NUMBER', 'PERSON', 'CRYPTO', 'AU_TFN', 'ORGANIZATION', 'UK_NHS', 'CREDIT_CARD', 'US_DRIVER_LICENSE',199# 'US_SSN', 'URL', 'AU_MEDICARE', 'DATE_TIME', 'NRP', 'US_PASSPORT', 'MEDICAL_LICENSE', 'US_BANK_NUMBER',200# 'IP_ADDRESS', 'IBAN_CODE', 'US_ITIN', 'AU_ACN', 'SG_NRIC_FIN', 'LOCATION', 'AU_ABN', 'EMAIL_ADDRESS']201# st.sidebar.text(list(get_supported_entities()))202 203st_threshold = st.sidebar.slider(204    label="Acceptance threshold", min_value=0.0, max_value=1.0, value=0.35205)206 207st_return_decision_process = st.sidebar.checkbox(208    "Add analysis explanations in json")209 210api_togg = st.sidebar.checkbox(label='API toggle', value=True)211 212# vertical space213st.sidebar.text("")214# vertical space215st.sidebar.text("")216 217 218st.sidebar.info(219    "Privy is an open source framework for synthetic data generation in protocol trace formats (json, sql, html etc). Presidio is an open source framework for PII detection and anonymization. "220    "For more info visit [privy](https://github.com/pixie-io/pixie/tree/main/src/datagen/pii/privy) and [aka.ms/presidio](https://aka.ms/presidio)"221)222 223 224# Main panel225if 'first_load' not in st.session_state:226    st.session_state['first_load'] = True227 228 229analyzer_load_state = st.info(230    "Starting analyzer and loading model...")231engine = analyzer_engine()232analyzer_load_state.empty()233 234# Initialization235# if 'bot' not in st.session_state:236#     st.sidebar.text("init...")237#     st.session_state['bot'] = ChatGPT()238#     init_prompt = "i'd like you to act like a snobby AI and tell me what you think of my structured data"239#     init_answer = st.session_state['bot'].ask(init_prompt)240 241# col?242# Store the initial value of widgets in session state243if "visibility" not in st.session_state:244    st.session_state.visibility = "visible"245    st.session_state.disabled = False246 247col1, col2 = st.columns(2)248 249with col1:250    st.subheader("Input")251 252    sys_name = st.text_area(253        label="Name of the system in question",254        value=option,255        height=1,256    )257 258    st_text = st.text_area(259        label= "Structured text used as input",260        value = """{ "@timestamp":"2022-06-08T16:54:58.849Z", "alienOTX":{ "firewall":{ "action":"Deny", "category":"AlienVaultFirewallNetworkRule", "icmp":{ "request":{ "code":"8" } }, "operation_name":"AzureFirewallNetworkRuleLog", "path": "http://www.example.com/ab001.zip", }, "resource":{ "group":"TEST-FW-RG", "id":"/SUBSCRIPTIONS/23103928-B2CF-472A-8CDB-FR7630006000011234567890189/RESOURCEGROUPS/TEST-FW-RG/PROVIDERS/MICROSOFT.NETWORK/AZUREFIREWALLS/TEST-FW01", "address":"172.24.0.4", "provider":"SonicWall", "number":"040084913373", "sentto": "willh@hotmail.com" }, "subscription_id":"4012888888881881-23103928-B2CF-472A-8CDB-0146E2849129" } }""",261        # value="SELECT shipping FROM users WHERE shipping = '201 Thayer St Providence RI 02912'"262        # "\n\n"263        # "{user: Willie Porter, ip: 192.168.2.80, email: willie@gmail.com}",264        height=300,265    )266    button = st.button("Detect and replace PII")267    st.text("""""")268 269with col2:270    st.subheader("Analyzed results with detected entities highlighted")271    # st.text("Output text with detected entities highlighted")272    with st.spinner("Analyzing..."):273        if button or st.session_state.first_load:274            option = sys_name275            st_analyze_results = analyze(276                text=st_text,277                entities=st_entities,278                language="en",279                score_threshold=st_threshold,280                return_decision_process=st_return_decision_process,281            )282            # """283            # Ugly hack that checks if last 2 chars as Z" and changes the end of the last entity to -1284            # This is done to prevent the anotation to inlcude the quotes for the date4 and breka the json donwtheroad285            # ### TODO: make this less hacky?286            # """287            for i in st_analyze_results:288                # st.write(i)289                # st.write(st_text[i.end - 2:i.end])290                if st_text[i.end-2:i.end] == 'Z"':# and i.type == "DATE_TIME":291                    i.end = i.end-1292                    continue293                if st_text[i.end-2:i.end] == "Z'":# and i.type == "DATE_TIME":294                    i.end = i.end-1295                    continue296                # if "'" in st_text[i.start:i.end]:297                #     st_analyze_results.remove(i)298                #     continue299                # if "," in st_text[i.start:i.end]:300                #     st_analyze_results.remove(i)301                #     continue302 303 304            annotated_tokens = annotate(st_text, st_analyze_results, st_entities)305            # annotated_tokens306            annotated_text(*annotated_tokens)307 308    # vertical space309    st.text("")310    st.text("")311 312    with st.expander("Show results with replaced PII and detailed results"):313        # st.subheader("Final results with tokens instead if PII")314        # vertical space315 316        if button or st.session_state.first_load:317            st_anonymize_results = anonymize(st_text, st_analyze_results)318            st.write(st_anonymize_results)319            # st.write(st_anonymize_results)320            # try:321            #     # st_anonymize_results = ast.literal_eval(st_anonymize_results)322            #     st.json(st_anonymize_results) #.replace("'", '"'))323            # except Json Parse Error as e:324            #     st.write(st_anonymize_results)325        # vertical space326        st.text("")327        st.subheader("Detailed Findings")328        if st_analyze_results:329            res_dicts = [r.to_dict() for r in st_analyze_results]330            for d in res_dicts:331                d['Value'] = st_text[d['start']:d['end']]332            df = pd.DataFrame.from_records(res_dicts)333            df = df[["entity_type", "Value", "score", "start", "end"]].rename(334                {335                    "entity_type": "Entity type",336                    "start": "Start",337                    "end": "End",338                    "score": "Confidence",339                },340                axis=1,341            )342 343            st.dataframe(df, width=1000)  # , height=500)344        else:345            st.text("No findings")346 347# st_analyze_results348# end of col349 350# After the columns351 352col5, col6 = st.columns(2)353prompt = "Write a summary for a {} event log, based on the given structured JSON input. Start with an executive summary with a short general description of what is a {}, and then focus on the Key Findings, Monitoring Summary,  Incident Summary, Threat Summary and Recommendations. Replace any random " \354            "strings and tokens in angular-brackets with an approximations to make it more human readable: \"{}\" ".format(355        option, option,356        st_anonymize_results)357 358with col5:359    st.subheader("Formatting")360 361    button_create = st.button("Create summary")362    st.markdown(363        "Start with an executive summary and describe what system the log came from, then focus on the Key Findings, Monitoring Summary,  Incident Summary, Threat Summary and Recommendations.")364    st.text("""""")365 366    with st.expander("Additional inputs"):367        st_prompt = st.text_area(368            label="Tokenized input with the formatted prompt",369            value=prompt,370            height=200,371        )372 373        write_results = ""374        st_output = st.text_area(375            label="Record results for later use",376            value=write_results,377            height=100,378        )379        button_save = st.button("Save summary to file?")380        st.text("""""")381 382        placeholder_table = st.empty()383        placeholder_table.write("")384 385        init_prompt = """I want you to act as a cyber security analyst expert. I will provide some specific information about concrete incidents, and it will be your job to come up with a coherent summery of the event, described in this log I give you. You can give a short description and then give strategies for protecting this system from malicious actors, based on the incident data I give you. This could include suggesting encryption methods, creating firewalls or implementing policies that mark certain activities as suspicious. Your summery would be used by decision makers to manage the situation, therefore make informed predictions and formulate them precisely in relation to the event I present to you."""386        st_init_prompt = st.text_area(387            label="Initial promopt to focus model",388            value=init_prompt,389            height=100,390        )391        button_reset = st.button("Reset model setup")392 393import random394 395with col6:396    st.subheader("Output incident summary")397 398# effect button_create(button2)399# with st.spinner("button_create..."):400if button_create:401    # load existing promp and results402    if (not api_togg):403        saved_prompt = dataframe[dataframe['Title'] == option]['Prompt'].values[0]404        saved_result = dataframe[dataframe['Title'] == option]['Result'].values[0]405    else:406        saved_prompt = ""407        saved_result = ""408    # check if match to current prompt409    # if re.sub(r"[\n\t\s]*", "", saved_prompt) == re.sub(r"[\n\t\s]*", "", st_prompt):410    md_results = ""411    with col6:412        x = st.empty()413        x.markdown("")414 415    # check if saved_prompt is not of a type float416    if (not isinstance(saved_prompt, float)) and (not api_togg):417        # st.write(saved_prompt)418        with col6:419            with st.spinner('Fetching results...'):420                time.sleep(random.uniform(2.1, 5.8))421                # st.write("Prompt already queried in the past, loading result from database")422                md_results = saved_result423                words = md_results.split()424                num_words = len(words)425                chunk_size = int(random.uniform(2, 6))426                str_placeholder = ""427 428                for i in range(0, num_words, chunk_size):429                    chunk = ' '.join(words[i:i + chunk_size])430                    str_placeholder = str_placeholder + " " + chunk431                    x.markdown(str_placeholder)432                    # x.markdown(chunk)433                    time.sleep(random.uniform(0.1, 0.6))434                x.markdown(saved_result)435    else:436        # st.write("New prompt, need GPT")437        with col6:438            with st.spinner('Generating, please wait...'):439                bot = ChatGPT()440                # init_answer = bot.ask(init_prompt)441 442                init_points = ""443                for chunk in bot.ask_stream(init_prompt):444                    init_points = init_points + "."445                    x.markdown(init_points)446 447                x.markdown("")448 449                # st_prompt = "tell me two facts about yourself"450                for chunk in bot.ask_stream(st_prompt):451                    md_results = md_results + chunk452                    x.markdown(md_results)453                    #check if last char of chunk is a new line454                    # if "\n" in chunk:455                    #     x.markdown(md_results)456                    # st.markdown(chunk)457                x.markdown(md_results)458                bot._cleanup()459                # md_results = bot.ask(st_prompt) #"Hello, could you tell what is {}?".format(option))460                # print(md_results)  # prints the response from chatGPT461 462                # st.write(st_prompt)463                # st.write(saved_prompt)464            # md_results = """No result found""" ##here GPT465    # with col6:466    #     # st.subheader("Output incident summary")467    #     st.markdown(md_results)468    placeholder_table.write((dataframe.loc[dataframe['Title'] == option]))469 470# if button_reset:471#     bot = ChatGPT()472#     bot._cleanup()473 474if button_save:475    # dataframe = pd.read_csv("structured-data-anonymizer/assets/df_data_short.csv")476    # save st_prompt and st_output to dataframe in row for Title = json_dict_option477    dataframe.loc[dataframe['Title'] == option, 'Prompt'] = st_prompt478    dataframe.loc[dataframe['Title'] == option, 'Result'] = md_results #st_output479    # st.write(json_dict_option)480    # write dataframe back to the csv file481    dataframe.to_csv("structured-data-anonymizer/assets/df_data_short.csv", index=False)482    st.write("Saved to file")483    st.write(dataframe.loc[dataframe['Title'] == option])484 485# end of document486st.session_state['first_load'] = True487 488 489 490class ToDictListEncoder(JSONEncoder):491    """Encode dict to json."""492 493    def default(self, o):494        """Encode to JSON using to_dict."""495        if o:496            return o.to_dict()497        return []498 499 500if st_return_decision_process:501    st.json(json.dumps(st_analyze_results, cls=ToDictListEncoder))502