From 5466c19a9ad45f4c16cc47987f0b901b44f48668 Mon Sep 17 00:00:00 2001 From: Fabrizio Turchi Date: Mon, 19 Feb 2024 14:32:21 +0100 Subject: [PATCH 01/22] Add modules utils under mix_utils folder to avoid duplication in JSON file and uniform some date and JSON content --- __pycache__/utils.cpython-39.pyc | Bin 0 -> 4324 bytes mix_utils/utils.py | 151 +++++++++++++++++++++++++++++++ 2 files changed, 151 insertions(+) create mode 100644 __pycache__/utils.cpython-39.pyc create mode 100644 mix_utils/utils.py diff --git a/__pycache__/utils.cpython-39.pyc b/__pycache__/utils.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b55d04e7e5673d67ff32c8e489ad5aaa580f62b1 GIT binary patch literal 4324 zcmb^!U31&U5qAJTL{qesICkwgDe78|7@MT*x*y?qJXWPRwkyh>h@MFpxflp{lwgqn zeFw@CLOt1i>vZ}9{9wJcFZmVy2YtK8_DQduK5?ew?j9hD`f&47U~X}DySKNyw|l#A zCnpOAJpZ}-cWzxUjDO-_{4rs051PCUfEkQ*j23=rhjdAc5aTO@nap}^FzXdxIcgm!2%u{McRJbc5T_? zp5OL8TT5wgxYAzZJh0u4;4a%4(_Zk7wgQ?iupf`af2=jK0lfnn*kPcpfR;dGqSAKQ zPT0$;-Q4hDXK8o28$|zyV>sm$sv`j&wzUk2YOrY#MhAyHb}~1>3;}<6QYBb%tS^M2V159HDm7KzX2NYppksZ7 zy8^s~J&bra<#6Q~|EFIs{fGdElFpxyWlW<*f8CGV&i|x&vJnP+oT7Yq$oD~oPA3V? zQ|$=H=rRWJ3!Z!6l1>_j_N*K1*qeU9&`D1?b=UQS2pXOj^V@AMICw4k&X@qlz3d=# z((B}ir{Vr5cF`26K;MBVeES)ONa8M>9jaP~tH7fkd3`a`?l59cU6#sv`ns_@<$ZQN&h*e5j@*R_|ASCVV(6vc>5QUMo4^Y} zZrKY1@a6UQ9_8^ZKX=8tOg3=6sPA@cZCf63fy(1Ky+o4!9-*>s4_B4*%96a(7MLFd=0uT2kOQDBR7#mX+g7D9>0#wx z4N(G$VXyWSb62hHt_eT(!`fo#^}FEIvL?fJ^wJf)+7qG9Q91|Kl5AJOTvtm3=Yi*Q zsj-y?@CLJ2p6o;rqP4ES?ertRBWoH~dpqik&NfLB#C#@E4r3}$3Zy_27+*S?rmJI1 z${Ce+oWSjJ$592x>4vNiDbrC!$9VzAmSS>_!$Qw-#1t^ss*ZuPIA;Ptn@OEmiDl{; z4`z-$NOlj}$tokSYZwBx#e}Y3NGroItrk>ao;~l&sL8jZSVDg7x|>|u8(}xZ^bz*q z_5i0vm%`#k+CV4Z$RSkQBJ66!H;pBT_g*L>U7M_P$c;eX4n-Fd$G7lQ8jZ^0V)Ma+ zdZSTaS-Gw|wssU{cyvq&wDKgh1yti-P){%d1riisC^pNnJSb3LXIPO?Re9YbgH1~;!N?qB z2H8EjYZ2o`^kNBX4^4k=L_eZKK>uTQ5&IVm=wD_ZkNTI`bkf&yPbVv&8p^>=pRf{027Op_gbr|@ zva@?s)BF^r*OGn0bYIJ5qg;O`;_DNbpQV_!prmEK!hW90jQU(nv47OKt88WrcP+*J zMdLnWvtzh&iu;?!ea@~YIM|)C8;QL^ntw$3w-caE1!WG3>8P4$08UwpUytsPg)rFS zB5|ltK(>W^sXY;DAuQ(Xe&C~{rQU`$8n?G4tE0Czp%k~f;N4IfZt)*+%Gty#lR6kH zKt<$>#r+wh4+;C*o&089BwIdk$Xc@5$D0_%h@5R4aw|~l$H6kxuEELL(wOV z<^6Z?42i8sb06#Mc;ffp%~ig5_WkYMxKx?deGSUZc%pJ^Zgu_6&AZKgykPh77ib^D zOyEjIsjks`?w_|g(B5^9ymHBxwI<%%#rl{rU? z6eyrkz?YCJY33BHqVKXMLd%TLA71~}F~N@9$>>6uqV=7Oe{ygqAHEcBU7Fsy=IExy zo739Oz)ec=-wtBid~s!9LUfzUBR{{QNMT4Ohh!qAtDgk_yEC&2+kpBAXq_gD1q+j6#fw)u0WG$%{;Ye;hDg>vLzPbqrFgPbv=6;3zff< P6cBeYh!Z||#Gn2bfM(gG literal 0 HcmV?d00001 diff --git a/mix_utils/utils.py b/mix_utils/utils.py new file mode 100644 index 0000000..d0e920e --- /dev/null +++ b/mix_utils/utils.py @@ -0,0 +1,151 @@ +import re +from datetime import datetime + +class CheckDuplicate(): + """It aims to avoid duplication in the JSON/CASE file generated by the parsers (UFED, AXIOM etc.) + """ + + def check_value(self, *args, value=None, list_values=None, list_objects=None, observable_generating_f=None): + """It checks if a specific value has been already generated related to an ObservableObject relying on + the list of its values. This is meant to avoid duplication in the JSON/CASE file generated by the + parsers (UFED, AXIOM etc.). + If the value is not in the list_values, a new ObservableObject is generated by using the function + observable_generating_f that returns, as a result, the new ObservableObject (e.g. uco-observable:ApplicationFacet, + uco-observable:AccountFacet, uco-location:LatLongCoordinatesFacet: drafting:SearchedItemFacet, "uco-observable:URLFacet, + uco-observable:ApplicationAccountFacet, uco-observable:DigitalAccountFacet, uco-observable:PhoneAccountFacet). + + Finally the new ObservableObject is added to the list_objects (any kind of ObservableObject maintains a different list). + If the value is already in the list_values, the ObservableObject list_objects[index] is returned. + + :param value: the value to be checked within the list_values + :param list_values: the current list of values + :param list_objects: the current list of a specific kind of ObservableObject + :param observable_generating_f: the function that will generate the corresponding kind of ObservableObject + :param *args: the actual parameter of the observable_generating_f function + :return: an Observableobject of a specific kind depending by the actual parameters + """ + if value in list_values: + idx = list_values.index(value) + observable_app = list_objects[idx] + else: + observable_app = observable_generating_f(*args) + list_values.append(value) + list_objects.append(observable_app) + + return observable_app + +class AdjustText(): + """It amends the data to either make it homogeneous or get rid of some dirty chracters extracted from the XML reports. + """ + def adjust_date(self, original_date=''): + """ + Amend the original date to convert it into a uniform format. The xsd:dateTime will have the format + YYYY-MM-DDTHH:MM:SS(+HH:MM). + """ + aMonths = { + 'Jan': '01', + 'Feb': '02', + 'Mar': '03', + 'Apr': '04', + 'May': '05', + 'Jun': '06', + 'Jul': '07', + 'Aug': '08', + 'Sep': '09', + 'Oct': '10', + 'Nov': '11', + 'Dec': '12' + } + + chars_to_replace = { + "/" : "-", + "(" : "-", + ")" : "-", + 'UTC': '', + 'AM': '', + 'PM': '' + } + + uniform_date = original_date.strip() + + if uniform_date == '': + return None + + for k,v in aMonths.items(): + if uniform_date.find(k) > -1: + uniform_date = uniform_date.replace(k, v) + break + for k,v in chars_to_replace.items(): + uniform_date = uniform_date.replace(k, v) + + uniform_date = uniform_date.replace(' ', 'T', 1) + + if re.search('^[0-9]{4}', uniform_date): + pass + else: + # when uniform_date is in Italian formato and the Year is placed before 'T' and + # composed of two digits, i.e. Year=YY + uniform_date = re.sub('-([0-9][0-9])T', '-20\g<1>T', uniform_date) + uniform_date = str(uniform_date[6:10]) + uniform_date[2:6] + uniform_date[0:2] + \ + uniform_date[10:] + + #start_tz = uniform_date.find("+") + #if start_tz > -1: + # uniform_date = uniform_date[:start_tz] + + date_chars = uniform_date[:10] # YYYY-MM-DD + date_chars = date_chars.replace(".", "-") + uniform_date = date_chars + uniform_date[10:] + + #if uniform_date[-1] == '-': + # uniform_date = uniform_date[0:-1] + + uniform_date = uniform_date.replace('.000', '').replace('.', ':') + + uniform_date = uniform_date.replace('.', ':') + + if re.search('T\d{2}\.', uniform_date): + uniform_date = uniform_date.replace('.', ':') + + if re.search('(\d{2}:\d{2}:\d{2})$', uniform_date): + pass + else: + uniform_date = re.sub('(\d{2}:\d{2})$', '\g<1>:00', uniform_date) + + if re.search('T(\d):', uniform_date): + uniform_date = re.sub('T(\d):', 'T0\g<1>:', uniform_date) + + if re.search(':(\d)', uniform_date): + uniform_date = re.sub(':(\d):', ':0\g<1>', uniform_date) + + if re.search('T\d{2}:\d{2}:\d{2}(.+)$', uniform_date): + uniform_date = re.sub('(T\d{2}:\d{2}:\d{2})(.+)$', '\g<1>', uniform_date) + + + if uniform_date.find('+') > -1: + uniform_date = datetime.strptime(uniform_date, + '%Y-%m-%dT%H:%M:%S.%f%z') + else: + uniform_date = datetime.strptime(uniform_date, + '%Y-%m-%dT%H:%M:%S') + + return uniform_date + + def adjust_json(self, original_json=''): + """ + It gets rid of some dirty characters not allowd in the JSON values. + """ + chars_to_replace = { + '"' : '', + '\n' : '', + '\r' : '', + '\t': '', + "\\'": '', + '\\': '' + } + conform_json = original_json.strip() + + for k,v in chars_to_replace.items(): + conform_json = conform_json.replace(k, v) + + return conform_json \ No newline at end of file From 6a14af931b0e16c63a0b653690820f1d18437a3d Mon Sep 17 00:00:00 2001 From: Fabrizio Turchi Date: Tue, 20 Feb 2024 13:24:37 +0100 Subject: [PATCH 02/22] Fix #22 issue, adding BrowserBookmarkFacet to observable module --- .gitignore | 2 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 333 bytes case_mapping/__pycache__/base.cpython-39.pyc | Bin 0 -> 7392 bytes .../case/__pycache__/__init__.cpython-39.pyc | Bin 0 -> 227 bytes .../__pycache__/investigation.cpython-39.pyc | Bin 0 -> 3878 bytes .../uco/__pycache__/__init__.cpython-39.pyc | Bin 0 -> 339 bytes .../uco/__pycache__/action.cpython-39.pyc | Bin 0 -> 2434 bytes .../uco/__pycache__/core.cpython-39.pyc | Bin 0 -> 2904 bytes .../uco/__pycache__/identity.cpython-39.pyc | Bin 0 -> 2308 bytes .../uco/__pycache__/location.cpython-39.pyc | Bin 0 -> 1786 bytes .../uco/__pycache__/observable.cpython-39.pyc | Bin 0 -> 48517 bytes .../uco/__pycache__/role.cpython-39.pyc | Bin 0 -> 2048 bytes .../uco/__pycache__/tool.cpython-39.pyc | Bin 0 -> 1167 bytes .../uco/__pycache__/types.cpython-39.pyc | Bin 0 -> 907 bytes case_mapping/uco/observable.py | 48 ++++++++++++++++-- 15 files changed, 47 insertions(+), 3 deletions(-) create mode 100644 case_mapping/__pycache__/__init__.cpython-39.pyc create mode 100644 case_mapping/__pycache__/base.cpython-39.pyc create mode 100644 case_mapping/case/__pycache__/__init__.cpython-39.pyc create mode 100644 case_mapping/case/__pycache__/investigation.cpython-39.pyc create mode 100644 case_mapping/uco/__pycache__/__init__.cpython-39.pyc create mode 100644 case_mapping/uco/__pycache__/action.cpython-39.pyc create mode 100644 case_mapping/uco/__pycache__/core.cpython-39.pyc create mode 100644 case_mapping/uco/__pycache__/identity.cpython-39.pyc create mode 100644 case_mapping/uco/__pycache__/location.cpython-39.pyc create mode 100644 case_mapping/uco/__pycache__/observable.cpython-39.pyc create mode 100644 case_mapping/uco/__pycache__/role.cpython-39.pyc create mode 100644 case_mapping/uco/__pycache__/tool.cpython-39.pyc create mode 100644 case_mapping/uco/__pycache__/types.cpython-39.pyc diff --git a/.gitignore b/.gitignore index 69f1c53..e91b714 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,8 @@ .venv/ venv/ +*wpr +*wpu # Build Artifacts build/ dist/ diff --git a/case_mapping/__pycache__/__init__.cpython-39.pyc b/case_mapping/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..536b001f6128d36fa85b9e6597c000f164faf5fa GIT binary patch literal 333 zcmYk1O-chX7>1L~)Q;AIxatv{#Y`?BqNq!yP}~fZkWNxfnf#Dsl=d{b^fDeGTS4d* zT$yMoevs$;;axnTn6GWO53{I4u7m+`r?2nHDJKm!?KS%+lSWHN7Z2sm5~ zUJqFQ2Ad&+Gf)n;F%9mwFtS7;Z>i%U>s!4!PGcphF+x3-MK~4GXzM$%s3rH5QBT9+ z{uY2u0yd8b#Xc~6=GF|qm)hfp5q4>Gk8fCR&0dEH@fX5SKeW#m;QxX zUK^`dyp5OZtJ&=@s=Gh0YP$AMTH)kMcCMHZp@b)-HtSHFOU?R@U!)JZWLKaQ^aB`! BUzz{_ literal 0 HcmV?d00001 diff --git a/case_mapping/__pycache__/base.cpython-39.pyc b/case_mapping/__pycache__/base.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f02f233f1243e899d636ae2919ca76942761396b GIT binary patch literal 7392 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 Rd9Dy^Wuv@z>gUSDe*h}>cUu4e literal 0 HcmV?d00001 diff --git a/case_mapping/case/__pycache__/__init__.cpython-39.pyc b/case_mapping/case/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e6ce1542702a9474568e803522cc5f3acda19994 GIT binary patch literal 227 zcmYe~<>g`kf@c!vQe}YjV-N=!FabFZKwK;UBvKes7;_kM8KW2(8B&;n88n$+G6ID) z8E^4s=9Q%umt>|VmSpDV`DrrUVkiP>UCB_y0;IsiuO$7@;?$yI{j|iSqRgtye0`Vv zWHa^HWN5Qtdzv`wYYk0DM+F+5i9m literal 0 HcmV?d00001 diff --git a/case_mapping/case/__pycache__/investigation.cpython-39.pyc b/case_mapping/case/__pycache__/investigation.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f0722ede78795905903200c25d0e323bf6634d79 GIT binary patch literal 3878 zcmb_eTW=Fb6yB?|cAOAG#I3X)0i{@SoC_5K3n9`#E2Lr&2`~Gy+U$&zMfR>cGfs%* zK7qtj`v*#^MCuEFraxdG`ckP9e<4y!&zW7@o8VB1GS-Y|W@qM{IhXGne`>0vLHXt#>t*yD5j(wx0w++{TzDcc?*|uCu*S^)LO$#qHT42_5 z)3sqdK^+)7YFvPEk(OXwQsW6`vjUr-ldnwIq2)ELGW7-|X%#(js4tle+YI~o--{#m z#z;SwA8tHlfxH*VQ0@k}BVX)nAg)7)X|7H+*Px}BT2rS6HD456(3sjUG}od9IsyMS zb!ZX(3okWGb0h}iXqyQcZu&CZW-EaV zLbtTO_S9%&Avsg(d?epxA);YwLyb|UxM3~NLE%N66O^m zzS4+0W$hk`l}(7;@+IM{!SJQ`z`i4QJFH?HOm%~JK8QK1qX!S> zhC|9kz{3tkbj9eY`P8p^IMNdfXZTnof z)7~Iw)i{~*I~{aMYNtveu>@;16AQITCUZ(8rBSdX&cKUqA>H^|2O(b2@`doc*V@76 z!V|%`SZMkiJnV(>!reINwlUThMBJ1+K4-NKj}aim0%nG4<{yAIVYE5_Xjg8<(E=LN zYo~K+uyDw-wazXFMAV9y+tbi!rIPOGr=dD;3uf=a7|YiNMhSG>kQMABLaag+GtfZx z13pmWN6d5+GTrK3U1L(EfXWF>)HG_cY#Rn>Eiu75U=Z6wA2*=qco1l~?X}KN!0G(- zusH+0q%4^91}6OkW&~oWX7tFo8{`*%>|0>KgoqV2h#~|Behw6Aa8!zaip>$#r%{_x zWPA=w2FhPNPWjRDK;0AeU_l2UEhr~cp(6pPr9t)Q`o8{yL5;q#XYPYLUKs$~R$u=a zK=>CE0Ng+*Zn)-?_0?YanV@xyamaFuQZK3~;+@4>6FPESSMbY}SVd#Z_X02l)FAIW--M~_DFheoVAVW)@ z*N$noh5fSUec$z4=@%8?{4JkB^Xj1IS;R{G;<8??ewf; zIhL9aVE$0SCj=IOa)xAV<;hr)t_60slas5EmO0-dn7|0o^KkRkHkF!8t?H`Y80uf#6i~A{C6tI*x{7j^$QhghM+9RP>}L z3Q#CA@X!U>dn&s|`i^-7d=f_jT@bgnL4VkE$Q{)@TEfM`2#DP$sduUacIT|A$Q+(z zN5v`b&SU}tz}^n95NeluWFCEq9e_RGNBME2APBGoX{vX2%xV=G93iApvG^qXr&&J* zJ@-^b(0Sum>=c-gsS-QgeiHlT{3@={K;gB51fAiDDFEMhq(P(h=+gPV!1g%0HI2FgB8UjAAU{iypQ7I}~b*;{>?4`VF zZ9txI^o}AS6%8DByG0suK+Tx%<|Gc|HgRsH)OZ_TX)1Wj2K>yK%QI(YifI7<3zvTn Axc~qF literal 0 HcmV?d00001 diff --git a/case_mapping/uco/__pycache__/__init__.cpython-39.pyc b/case_mapping/uco/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7f9e577e9c9e7dc458dff6d0f01c23e0b55cb57d GIT binary patch literal 339 zcmYk%%}T^D5CGuxKfAJbU!mY`UO+@qk1oREC6|(>S&cNElB7y~BVWb`$W;(Ld-JrD z^x9IKECl)E4K@nEW3pCYKoQ4CzRh^u#1QF)2??#xs-i+!VYpB`;0I zD?>R=NSE$NoqZ>Gq^6-1(rXw(F2Slmb*Bce{TyfyQXF@d&^l#bMBA&7TZnpqJ`{c) zmD|>fKy3Vo%_4cEh?J2kLL&?0o!FDOj{FMtfcHAK7p>KA8rTiU$>8&jIoSJGVbx}| z@T??OuCI2tN7i>@9QE+HzMuUQ3``2A8uM3UlLXc@deFXUHX}B1ZlIfbbrGBW!5{}Q CSz9*% literal 0 HcmV?d00001 diff --git a/case_mapping/uco/__pycache__/action.cpython-39.pyc b/case_mapping/uco/__pycache__/action.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..747595ffa02d380de6da61676d75c951f4aa4162 GIT binary patch literal 2434 zcmdT`&5qkP5GG~$H=AJ7-2~~S;GqT9fY%0k=wT5QanmHop>DBBF1iR@iL#Z*q)SpZ zZ1}bTa?2C+&=$RHAEJ-Y2hg=Zfj&aFhjvDiZEd;*dg!GzL{LM{%s0dNM&>tfb_k4L z@BJ)fi;!RNr8-=Ac>s^;!4#5^CS*!II(0mUk|!i|!rE&R)&xDTd9~0LwU~NtSbs*k zjo%zl={nhs$2<`Bk+iX0X17y0#>1J^JM_1u8csf49Qgb28_kQ=>TJy8MNhmwWzSV*CH!Zy+(jTLn;s$Q{K0+r8>PL-PW*k@^+ zgoqJBc&-+*`z}Hu=do6@0&zKmfFoAfU`Gi*>wz$e1MxQtT9`Dk=7bGdQr=M^%H5`R zmfFgtnJs`zmIms$2>fLD4rOv4t?Cafpjq9m9IU|V)>ns>o$bY~?o_0^8(1Aw#G4#m z-mVA>*jGeF9gVtfRyQel$XdQ1%h>vU*7o6U{W;erJM^Urg})_QF6}lm*N7zY+FN8^ zX9p*1Z7_H@DnVtrt21E^uWX$@GJ!B4bzi|hBhU;L$um|Sk+z~?KM`Q%|wdX@(g$%JG%!Q-( zK%Th>NWTZmVmd(Kt?j{_L?407V7^FW_2Km?zN+w_B)TZH|5Kvz8#>a5Fl#K*f68jf zpkp}l@3LAdbQ`05bX&-G`~(2sJH9_vVVdB2&-cGhc~b1;gwa^x`W{a2!<5}DDI16+ z@%;+GeLRA9ukl|FJ~<~^HK4)f_xmm8&`#5#`5BF59ChoNdj_P{SRREpRL(nFPTB*1G1w8=bl79wq_7w?)Iu{?{~%!&-prlp6ZS^mQW zm8LnZf7*S-LUk`uks438vLp?{@BBdYB@hveLlApqpiLaI1B4OJpu{t2{Tb;QxxK1q zF`I&wr{sy@m4GkPwVW>lU!m2UUjx2I&*XgN8L^311+UhpxTCkDv80kY@8p$q!knll?;445+vqQv>=?!$Xd0h`m~!VxSL zOfo<)={tcF29h~JLY+uSCkRzEid4jag(aSYE!*=$^_v5w(tX!OEuJVI^ZvNaMc)m% zU|03KxGiCSzIRd(G~Fl}F{z?{pdy~Q`rPc)G!yacG*`bpTRc2GY#-jrmv8Rv?YTR< z?(S|&&|XW96BRseCGx7ic2anEXJ^OVx#{MT;eZW;)BMYls24FBj=N0EoyH4gA$JiD z^zYV*YrR$tv2gYKt0_ekpG1vUR*ZQ#gK)25QXD~O+;qQw@X5m>ESe2nKFC${S&Y}#E8#gcL+)7+ zMV<{r>Ka5wtB4F;K^~u5*O5DZ!{Y1sVG1ckTW_G$0>WYkanV)W$uC<7EpFghk0sTW zgAl@#JytIp$2Rwi;-gCksMoag`g|t|hD>kIKV{0oub7Y!%r6(Kl*v#;DTcVV4kFrV z+S8KEQYNMq-;a_=`Tn%j&7zo2tG-mi9|b}-OH*62xaTzxD!;fgq>%8?ypLe`Xx^)% zRew~Not{~;c4WYLei#Q*`u-7l`HlM}1eJ7qL03dN;_k;h%!ZH-(v`fY4gBHFS^oB$D#@)7;Cb84+yE%jIwDOulf74wpB4I zh0NdfKjn&T=thBc`xuwdz_VrpdlMG>`!MBsAW*QOyY-EU@ssg6c@Bl25-87R2jZ+R zf^+}~T=KzHmQa5Y4#4JjJTgTcPMO-+*95kbf^6a9_DoFy*7yMVd2QH{=ms(M<(aFe z$*t_R32s~Z*LefDODXgW39v%}P+1x?hxeeR$ShXZWz$jIQP6neNGjamnMyP0D?P!7 z&{6XK!jt)F`)MEo*nn&9J9mptRbX(#flf7p1Nfd|pq#+UarJ3C*IB#YhP@QsCI7CS@b5H@rrm+jtYM+TVgrS$=KDiVvl#ih?|+{Kaj}!+ literal 0 HcmV?d00001 diff --git a/case_mapping/uco/__pycache__/identity.cpython-39.pyc b/case_mapping/uco/__pycache__/identity.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..23f8c858ab5337ea6bebf39aac3ac827a50f735d GIT binary patch literal 2308 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 C_Q%-( literal 0 HcmV?d00001 diff --git a/case_mapping/uco/__pycache__/role.cpython-39.pyc b/case_mapping/uco/__pycache__/role.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..baaf4c71398de52808f4b648cbf96871ab519960 GIT binary patch literal 2048 zcmbVNPmdcl6!&B%X_DO_RBbEN1M-0bLE6oU%c??MmcpqEVp|T8ka28J<1!hiwr969 z%B@Jf@d@?-5|PP#3E$sPFJ`_M@ugM_Stc@U7#i0sMmJ0e3BJPqeP8L6HQ=ArDLlj-319;i=) za_>_vl)syL?Q;R5S6by6B<@}46qyH-pq*zV4l3KDdH9S(WFCRVei?mf5_Kl_Iwo*D zfWN3qwX$xlVEsZJwFZyZ-0~Hb$_cC2xc8Wzbf?tB;0v_87Foi{3B>Vy!HVA$Y!HsP@>n*|EAZ5;sfc z^-3MnbI1tij{2pdD1!m_&;drBs4pOS1O~D}MYDJWNYG2}=+@wlr<9|E`=E`Z|Br*P ziu*QTv{!`*L0&GkSiT7XEOujJCq%3#Yf`I2?d193^mE_{(7>B6#tI$QHQcf1`NOJY zDuuEN3e#D_7b@9NWIkUv;HW3-L<{Yc{7|Y`r|1wC5wF)N^6!=1;H#nuLe+-k)&-%o2M;@UJU_S#h~i|g&A0s7;-xY^3;?G1Kka5wf{2G>bBWDHSctQ;}t zExX{>m2WW#d`FJg^8=O|shCxc&Cq`r| zsHU4a6Z=bU)zR7-yeHi3^yK_*)g9ggo&b{$zsUX4q%#2*EwHPS%>b_%*8OOm+X1`| zeS9W9fR5Z62g6|4>jlmCXY_I~s^)*gXwaDb$eE)puG8^Z+W>aJ8_@u~$BY3? zS%Twz#va23U$0b-%Sn@|P)Wj=J%AAE?mCWcU>8D%kFC22-7thF?Dg-DO2?wo*)%F6 nd{ivHvz5FYN#MQk#^xRSl_#`c4sX{1eiXpWp^^RY?GOI}Nx5`C literal 0 HcmV?d00001 diff --git a/case_mapping/uco/__pycache__/tool.cpython-39.pyc b/case_mapping/uco/__pycache__/tool.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..476ea016cf581137ef5d966e8580e9098663ee94 GIT binary patch literal 1167 zcmZuwL2uJA6tVU(cq<7nQG}8RARA>gR4N~&4Zox^&Tqm8%#nEl#-a=K6@Nn* zn67}~K3bvEAVU(Z@G4k^tB8kRqVM<3GLxwxDuV)FIx~V3SrAr$oeN{-h*_zMHSpe)X*Ti2v|tmlM_eoCITQ!$ zadt$1)PGF2#e12##CGpigj5S=m99Xzn2TIZg@*2&C=Kt371DSD0L?6!YQ<-%Fc5(# z-|2iJM`KG00mMNc;~cbbnOZUyT70;*tzArO;?DELf#a>EbD<6BuNc+UR7 zyN=S{5X`c1;IMnq(&tm9#h%-x<<`2aMqA!nXWZ%`tlB85cWBTH%Z8c3=9EoJWg#u4 zWs4fC>6~ffPL&aPmb4s%|1n9&yLFR#!cnRxYTzpjFf`4XF_d1R>$mZ-0m*U3PPAM~ z6~9pFY$6J4Vxuy9#%MRW1g3vE$O8 zF7zEZ+wQQN`(RMJjhna`1lT`+qMK!R=nDTS>_r0@APzLU%>&JCN+)mza_4s_eK%uy zHRGYnZmlI1c}}Um3r#B1a$d(7cgNg?HO@2xbmKGHMT1^cMkl~syKC?BWs6G%m_u(nz8J literal 0 HcmV?d00001 diff --git a/case_mapping/uco/__pycache__/types.cpython-39.pyc b/case_mapping/uco/__pycache__/types.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2f6fb43b743ba74bb633524e4323ff3925645352 GIT binary patch literal 907 zcmZ`%&2AGh5VrTHO49~7!Ub7{Lsuf%JE~Btws0ass<`BU<*p~x;Hy_oC?#)*jAZc(26O?1aqqVsDW*Ju#@~V z)GyeX&{N1*C6s07Ct!j36V&B-xP_q3TcJvj%o`@kRYUkvX;mn%YbIgb7C7+Cj2EbG zu&TU%HZ4rw*A=*z_rAs~-3=94H0#|~hxQ|5(mz$c4bjzi`x!$DQsHT=F5)dh#7UD~0ut$Udd zZdhc|aEKd&tV%HfWYy(-V&qf@f0OgmsnCm)wnNifaw Date: Tue, 20 Feb 2024 13:49:51 +0100 Subject: [PATCH 03/22] Fix #3 issue, changing uco-tool:creator property as a reference to an uco-identity class --- case_mapping/uco/tool.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/case_mapping/uco/tool.py b/case_mapping/uco/tool.py index d7b6913..22c2d22 100644 --- a/case_mapping/uco/tool.py +++ b/case_mapping/uco/tool.py @@ -8,7 +8,7 @@ def __init__( """ The Uco tool is a way to define the specifics of a tool used in an investigation :param tool_name: The name of the tool (e.g., "exiftool") - :param tool_creator: The developer and or organisation that produces this tool {might need to add a dict here} + :param tool_creator: An ObservableObject The developer and or organisation that produces this tool {might need to add a dict here} :param tool_type: The type of tool :param tool_version: The version of the tool """ @@ -18,10 +18,10 @@ def __init__( **{ "uco-core:name": tool_name, "uco-tool:version": tool_version, - "uco-tool:toolType": tool_type, - "uco-tool:creator": tool_creator, + "uco-tool:toolType": tool_type } ) + self._node_reference_vars(**{"uco-tool:creator": tool_creator} ) directory = {"uco-tool:Tool": Tool} From bb70ee757d96d29759363bf4682605cb7203f16e Mon Sep 17 00:00:00 2001 From: Fabrizio Turchi Date: Tue, 20 Feb 2024 14:34:38 +0100 Subject: [PATCH 04/22] Fix #13 issue, replacing EventFacet class with EventRecordFacet class --- case_mapping/uco/observable.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/case_mapping/uco/observable.py b/case_mapping/uco/observable.py index 9ecc181..49e2732 100644 --- a/case_mapping/uco/observable.py +++ b/case_mapping/uco/observable.py @@ -1057,6 +1057,8 @@ def __init__( cyber_action=None, computer_name=None, created_time=None, + start_time=None, + end_time=None ): """ An event facet is a grouping of characteristics unique to something that happens in a digital context @@ -1065,10 +1067,12 @@ def __init__( :param event_text: The textual representation of the event. :param event_id: The identifier of the event. :param cyber_action: The action taken in response to the event. - :param computer_name: A name of the computer on which the log entry was created. + :param created_time: The date and time at which the observable object being characterized was created. + :param start_time: The date and time at which the observable object being characterized started. + :param end_time: The date and time at which the observable object being characterized ended. """ super().__init__() - self["@type"] = "uco-observable:EventFacet" + self["@type"] = "uco-observable:EventRecordFacet" self._str_vars( **{ "uco-observable:eventType": event_type, @@ -1078,7 +1082,8 @@ def __init__( } ) self._node_reference_vars(**{"uco-observable:cyberAction": cyber_action}) - self._datetime_vars(**{"uco-observable:observableCreatedTime": created_time}) + self._datetime_vars(**{"uco-observable:startTime": start_time, + "uco-observable:endTime": end_time}) class ObservableRelationship(ObjectEntity): From fc0ac4de7af63a6775f5b153bc8f24482355a2d9 Mon Sep 17 00:00:00 2001 From: Fabrizio Turchi Date: Tue, 20 Feb 2024 15:28:11 +0100 Subject: [PATCH 05/22] Change .gitignore to ignore the .pyc files nad the __pycache__ folder --- .gitignore | 5 +++++ __pycache__/utils.cpython-39.pyc | Bin 4324 -> 0 bytes .../__pycache__/__init__.cpython-39.pyc | Bin 333 -> 0 bytes case_mapping/__pycache__/base.cpython-39.pyc | Bin 7392 -> 0 bytes .../case/__pycache__/__init__.cpython-39.pyc | Bin 227 -> 0 bytes .../__pycache__/investigation.cpython-39.pyc | Bin 3878 -> 0 bytes .../uco/__pycache__/__init__.cpython-39.pyc | Bin 339 -> 0 bytes .../uco/__pycache__/action.cpython-39.pyc | Bin 2434 -> 0 bytes .../uco/__pycache__/core.cpython-39.pyc | Bin 2904 -> 0 bytes .../uco/__pycache__/identity.cpython-39.pyc | Bin 2308 -> 0 bytes .../uco/__pycache__/location.cpython-39.pyc | Bin 1786 -> 0 bytes .../uco/__pycache__/observable.cpython-39.pyc | Bin 48517 -> 0 bytes .../uco/__pycache__/role.cpython-39.pyc | Bin 2048 -> 0 bytes .../uco/__pycache__/tool.cpython-39.pyc | Bin 1167 -> 0 bytes .../uco/__pycache__/types.cpython-39.pyc | Bin 907 -> 0 bytes 15 files changed, 5 insertions(+) delete mode 100644 __pycache__/utils.cpython-39.pyc delete mode 100644 case_mapping/__pycache__/__init__.cpython-39.pyc delete mode 100644 case_mapping/__pycache__/base.cpython-39.pyc delete mode 100644 case_mapping/case/__pycache__/__init__.cpython-39.pyc delete mode 100644 case_mapping/case/__pycache__/investigation.cpython-39.pyc delete mode 100644 case_mapping/uco/__pycache__/__init__.cpython-39.pyc delete mode 100644 case_mapping/uco/__pycache__/action.cpython-39.pyc delete mode 100644 case_mapping/uco/__pycache__/core.cpython-39.pyc delete mode 100644 case_mapping/uco/__pycache__/identity.cpython-39.pyc delete mode 100644 case_mapping/uco/__pycache__/location.cpython-39.pyc delete mode 100644 case_mapping/uco/__pycache__/observable.cpython-39.pyc delete mode 100644 case_mapping/uco/__pycache__/role.cpython-39.pyc delete mode 100644 case_mapping/uco/__pycache__/tool.cpython-39.pyc delete mode 100644 case_mapping/uco/__pycache__/types.cpython-39.pyc diff --git a/.gitignore b/.gitignore index e91b714..ad89e2d 100644 --- a/.gitignore +++ b/.gitignore @@ -9,8 +9,13 @@ .venv/ venv/ +# Wing IDE *wpr *wpu + +__pycache__/ +*.pyc + # Build Artifacts build/ dist/ diff --git a/__pycache__/utils.cpython-39.pyc b/__pycache__/utils.cpython-39.pyc deleted file mode 100644 index b55d04e7e5673d67ff32c8e489ad5aaa580f62b1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4324 zcmb^!U31&U5qAJTL{qesICkwgDe78|7@MT*x*y?qJXWPRwkyh>h@MFpxflp{lwgqn zeFw@CLOt1i>vZ}9{9wJcFZmVy2YtK8_DQduK5?ew?j9hD`f&47U~X}DySKNyw|l#A zCnpOAJpZ}-cWzxUjDO-_{4rs051PCUfEkQ*j23=rhjdAc5aTO@nap}^FzXdxIcgm!2%u{McRJbc5T_? zp5OL8TT5wgxYAzZJh0u4;4a%4(_Zk7wgQ?iupf`af2=jK0lfnn*kPcpfR;dGqSAKQ zPT0$;-Q4hDXK8o28$|zyV>sm$sv`j&wzUk2YOrY#MhAyHb}~1>3;}<6QYBb%tS^M2V159HDm7KzX2NYppksZ7 zy8^s~J&bra<#6Q~|EFIs{fGdElFpxyWlW<*f8CGV&i|x&vJnP+oT7Yq$oD~oPA3V? zQ|$=H=rRWJ3!Z!6l1>_j_N*K1*qeU9&`D1?b=UQS2pXOj^V@AMICw4k&X@qlz3d=# z((B}ir{Vr5cF`26K;MBVeES)ONa8M>9jaP~tH7fkd3`a`?l59cU6#sv`ns_@<$ZQN&h*e5j@*R_|ASCVV(6vc>5QUMo4^Y} zZrKY1@a6UQ9_8^ZKX=8tOg3=6sPA@cZCf63fy(1Ky+o4!9-*>s4_B4*%96a(7MLFd=0uT2kOQDBR7#mX+g7D9>0#wx z4N(G$VXyWSb62hHt_eT(!`fo#^}FEIvL?fJ^wJf)+7qG9Q91|Kl5AJOTvtm3=Yi*Q zsj-y?@CLJ2p6o;rqP4ES?ertRBWoH~dpqik&NfLB#C#@E4r3}$3Zy_27+*S?rmJI1 z${Ce+oWSjJ$592x>4vNiDbrC!$9VzAmSS>_!$Qw-#1t^ss*ZuPIA;Ptn@OEmiDl{; z4`z-$NOlj}$tokSYZwBx#e}Y3NGroItrk>ao;~l&sL8jZSVDg7x|>|u8(}xZ^bz*q z_5i0vm%`#k+CV4Z$RSkQBJ66!H;pBT_g*L>U7M_P$c;eX4n-Fd$G7lQ8jZ^0V)Ma+ zdZSTaS-Gw|wssU{cyvq&wDKgh1yti-P){%d1riisC^pNnJSb3LXIPO?Re9YbgH1~;!N?qB z2H8EjYZ2o`^kNBX4^4k=L_eZKK>uTQ5&IVm=wD_ZkNTI`bkf&yPbVv&8p^>=pRf{027Op_gbr|@ zva@?s)BF^r*OGn0bYIJ5qg;O`;_DNbpQV_!prmEK!hW90jQU(nv47OKt88WrcP+*J zMdLnWvtzh&iu;?!ea@~YIM|)C8;QL^ntw$3w-caE1!WG3>8P4$08UwpUytsPg)rFS zB5|ltK(>W^sXY;DAuQ(Xe&C~{rQU`$8n?G4tE0Czp%k~f;N4IfZt)*+%Gty#lR6kH zKt<$>#r+wh4+;C*o&089BwIdk$Xc@5$D0_%h@5R4aw|~l$H6kxuEELL(wOV z<^6Z?42i8sb06#Mc;ffp%~ig5_WkYMxKx?deGSUZc%pJ^Zgu_6&AZKgykPh77ib^D zOyEjIsjks`?w_|g(B5^9ymHBxwI<%%#rl{rU? z6eyrkz?YCJY33BHqVKXMLd%TLA71~}F~N@9$>>6uqV=7Oe{ygqAHEcBU7Fsy=IExy zo739Oz)ec=-wtBid~s!9LUfzUBR{{QNMT4Ohh!qAtDgk_yEC&2+kpBAXq_gD1q+j6#fw)u0WG$%{;Ye;hDg>vLzPbqrFgPbv=6;3zff< P6cBeYh!Z||#Gn2bfM(gG diff --git a/case_mapping/__pycache__/__init__.cpython-39.pyc b/case_mapping/__pycache__/__init__.cpython-39.pyc deleted file mode 100644 index 536b001f6128d36fa85b9e6597c000f164faf5fa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 333 zcmYk1O-chX7>1L~)Q;AIxatv{#Y`?BqNq!yP}~fZkWNxfnf#Dsl=d{b^fDeGTS4d* zT$yMoevs$;;axnTn6GWO53{I4u7m+`r?2nHDJKm!?KS%+lSWHN7Z2sm5~ zUJqFQ2Ad&+Gf)n;F%9mwFtS7;Z>i%U>s!4!PGcphF+x3-MK~4GXzM$%s3rH5QBT9+ z{uY2u0yd8b#Xc~6=GF|qm)hfp5q4>Gk8fCR&0dEH@fX5SKeW#m;QxX zUK^`dyp5OZtJ&=@s=Gh0YP$AMTH)kMcCMHZp@b)-HtSHFOU?R@U!)JZWLKaQ^aB`! BUzz{_ diff --git a/case_mapping/__pycache__/base.cpython-39.pyc b/case_mapping/__pycache__/base.cpython-39.pyc deleted file mode 100644 index f02f233f1243e899d636ae2919ca76942761396b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7392 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 Rd9Dy^Wuv@z>gUSDe*h}>cUu4e diff --git a/case_mapping/case/__pycache__/__init__.cpython-39.pyc b/case_mapping/case/__pycache__/__init__.cpython-39.pyc deleted file mode 100644 index e6ce1542702a9474568e803522cc5f3acda19994..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 227 zcmYe~<>g`kf@c!vQe}YjV-N=!FabFZKwK;UBvKes7;_kM8KW2(8B&;n88n$+G6ID) z8E^4s=9Q%umt>|VmSpDV`DrrUVkiP>UCB_y0;IsiuO$7@;?$yI{j|iSqRgtye0`Vv zWHa^HWN5Qtdzv`wYYk0DM+F+5i9m diff --git a/case_mapping/case/__pycache__/investigation.cpython-39.pyc b/case_mapping/case/__pycache__/investigation.cpython-39.pyc deleted file mode 100644 index f0722ede78795905903200c25d0e323bf6634d79..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3878 zcmb_eTW=Fb6yB?|cAOAG#I3X)0i{@SoC_5K3n9`#E2Lr&2`~Gy+U$&zMfR>cGfs%* zK7qtj`v*#^MCuEFraxdG`ckP9e<4y!&zW7@o8VB1GS-Y|W@qM{IhXGne`>0vLHXt#>t*yD5j(wx0w++{TzDcc?*|uCu*S^)LO$#qHT42_5 z)3sqdK^+)7YFvPEk(OXwQsW6`vjUr-ldnwIq2)ELGW7-|X%#(js4tle+YI~o--{#m z#z;SwA8tHlfxH*VQ0@k}BVX)nAg)7)X|7H+*Px}BT2rS6HD456(3sjUG}od9IsyMS zb!ZX(3okWGb0h}iXqyQcZu&CZW-EaV zLbtTO_S9%&Avsg(d?epxA);YwLyb|UxM3~NLE%N66O^m zzS4+0W$hk`l}(7;@+IM{!SJQ`z`i4QJFH?HOm%~JK8QK1qX!S> zhC|9kz{3tkbj9eY`P8p^IMNdfXZTnof z)7~Iw)i{~*I~{aMYNtveu>@;16AQITCUZ(8rBSdX&cKUqA>H^|2O(b2@`doc*V@76 z!V|%`SZMkiJnV(>!reINwlUThMBJ1+K4-NKj}aim0%nG4<{yAIVYE5_Xjg8<(E=LN zYo~K+uyDw-wazXFMAV9y+tbi!rIPOGr=dD;3uf=a7|YiNMhSG>kQMABLaag+GtfZx z13pmWN6d5+GTrK3U1L(EfXWF>)HG_cY#Rn>Eiu75U=Z6wA2*=qco1l~?X}KN!0G(- zusH+0q%4^91}6OkW&~oWX7tFo8{`*%>|0>KgoqV2h#~|Behw6Aa8!zaip>$#r%{_x zWPA=w2FhPNPWjRDK;0AeU_l2UEhr~cp(6pPr9t)Q`o8{yL5;q#XYPYLUKs$~R$u=a zK=>CE0Ng+*Zn)-?_0?YanV@xyamaFuQZK3~;+@4>6FPESSMbY}SVd#Z_X02l)FAIW--M~_DFheoVAVW)@ z*N$noh5fSUec$z4=@%8?{4JkB^Xj1IS;R{G;<8??ewf; zIhL9aVE$0SCj=IOa)xAV<;hr)t_60slas5EmO0-dn7|0o^KkRkHkF!8t?H`Y80uf#6i~A{C6tI*x{7j^$QhghM+9RP>}L z3Q#CA@X!U>dn&s|`i^-7d=f_jT@bgnL4VkE$Q{)@TEfM`2#DP$sduUacIT|A$Q+(z zN5v`b&SU}tz}^n95NeluWFCEq9e_RGNBME2APBGoX{vX2%xV=G93iApvG^qXr&&J* zJ@-^b(0Sum>=c-gsS-QgeiHlT{3@={K;gB51fAiDDFEMhq(P(h=+gPV!1g%0HI2FgB8UjAAU{iypQ7I}~b*;{>?4`VF zZ9txI^o}AS6%8DByG0suK+Tx%<|Gc|HgRsH)OZ_TX)1Wj2K>yK%QI(YifI7<3zvTn Axc~qF diff --git a/case_mapping/uco/__pycache__/__init__.cpython-39.pyc b/case_mapping/uco/__pycache__/__init__.cpython-39.pyc deleted file mode 100644 index 7f9e577e9c9e7dc458dff6d0f01c23e0b55cb57d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 339 zcmYk%%}T^D5CGuxKfAJbU!mY`UO+@qk1oREC6|(>S&cNElB7y~BVWb`$W;(Ld-JrD z^x9IKECl)E4K@nEW3pCYKoQ4CzRh^u#1QF)2??#xs-i+!VYpB`;0I zD?>R=NSE$NoqZ>Gq^6-1(rXw(F2Slmb*Bce{TyfyQXF@d&^l#bMBA&7TZnpqJ`{c) zmD|>fKy3Vo%_4cEh?J2kLL&?0o!FDOj{FMtfcHAK7p>KA8rTiU$>8&jIoSJGVbx}| z@T??OuCI2tN7i>@9QE+HzMuUQ3``2A8uM3UlLXc@deFXUHX}B1ZlIfbbrGBW!5{}Q CSz9*% diff --git a/case_mapping/uco/__pycache__/action.cpython-39.pyc b/case_mapping/uco/__pycache__/action.cpython-39.pyc deleted file mode 100644 index 747595ffa02d380de6da61676d75c951f4aa4162..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2434 zcmdT`&5qkP5GG~$H=AJ7-2~~S;GqT9fY%0k=wT5QanmHop>DBBF1iR@iL#Z*q)SpZ zZ1}bTa?2C+&=$RHAEJ-Y2hg=Zfj&aFhjvDiZEd;*dg!GzL{LM{%s0dNM&>tfb_k4L z@BJ)fi;!RNr8-=Ac>s^;!4#5^CS*!II(0mUk|!i|!rE&R)&xDTd9~0LwU~NtSbs*k zjo%zl={nhs$2<`Bk+iX0X17y0#>1J^JM_1u8csf49Qgb28_kQ=>TJy8MNhmwWzSV*CH!Zy+(jTLn;s$Q{K0+r8>PL-PW*k@^+ zgoqJBc&-+*`z}Hu=do6@0&zKmfFoAfU`Gi*>wz$e1MxQtT9`Dk=7bGdQr=M^%H5`R zmfFgtnJs`zmIms$2>fLD4rOv4t?Cafpjq9m9IU|V)>ns>o$bY~?o_0^8(1Aw#G4#m z-mVA>*jGeF9gVtfRyQel$XdQ1%h>vU*7o6U{W;erJM^Urg})_QF6}lm*N7zY+FN8^ zX9p*1Z7_H@DnVtrt21E^uWX$@GJ!B4bzi|hBhU;L$um|Sk+z~?KM`Q%|wdX@(g$%JG%!Q-( zK%Th>NWTZmVmd(Kt?j{_L?407V7^FW_2Km?zN+w_B)TZH|5Kvz8#>a5Fl#K*f68jf zpkp}l@3LAdbQ`05bX&-G`~(2sJH9_vVVdB2&-cGhc~b1;gwa^x`W{a2!<5}DDI16+ z@%;+GeLRA9ukl|FJ~<~^HK4)f_xmm8&`#5#`5BF59ChoNdj_P{SRREpRL(nFPTB*1G1w8=bl79wq_7w?)Iu{?{~%!&-prlp6ZS^mQW zm8LnZf7*S-LUk`uks438vLp?{@BBdYB@hveLlApqpiLaI1B4OJpu{t2{Tb;QxxK1q zF`I&wr{sy@m4GkPwVW>lU!m2UUjx2I&*XgN8L^311+UhpxTCkDv80kY@8p$q!knll?;445+vqQv>=?!$Xd0h`m~!VxSL zOfo<)={tcF29h~JLY+uSCkRzEid4jag(aSYE!*=$^_v5w(tX!OEuJVI^ZvNaMc)m% zU|03KxGiCSzIRd(G~Fl}F{z?{pdy~Q`rPc)G!yacG*`bpTRc2GY#-jrmv8Rv?YTR< z?(S|&&|XW96BRseCGx7ic2anEXJ^OVx#{MT;eZW;)BMYls24FBj=N0EoyH4gA$JiD z^zYV*YrR$tv2gYKt0_ekpG1vUR*ZQ#gK)25QXD~O+;qQw@X5m>ESe2nKFC${S&Y}#E8#gcL+)7+ zMV<{r>Ka5wtB4F;K^~u5*O5DZ!{Y1sVG1ckTW_G$0>WYkanV)W$uC<7EpFghk0sTW zgAl@#JytIp$2Rwi;-gCksMoag`g|t|hD>kIKV{0oub7Y!%r6(Kl*v#;DTcVV4kFrV z+S8KEQYNMq-;a_=`Tn%j&7zo2tG-mi9|b}-OH*62xaTzxD!;fgq>%8?ypLe`Xx^)% zRew~Not{~;c4WYLei#Q*`u-7l`HlM}1eJ7qL03dN;_k;h%!ZH-(v`fY4gBHFS^oB$D#@)7;Cb84+yE%jIwDOulf74wpB4I zh0NdfKjn&T=thBc`xuwdz_VrpdlMG>`!MBsAW*QOyY-EU@ssg6c@Bl25-87R2jZ+R zf^+}~T=KzHmQa5Y4#4JjJTgTcPMO-+*95kbf^6a9_DoFy*7yMVd2QH{=ms(M<(aFe z$*t_R32s~Z*LefDODXgW39v%}P+1x?hxeeR$ShXZWz$jIQP6neNGjamnMyP0D?P!7 z&{6XK!jt)F`)MEo*nn&9J9mptRbX(#flf7p1Nfd|pq#+UarJ3C*IB#YhP@QsCI7CS@b5H@rrm+jtYM+TVgrS$=KDiVvl#ih?|+{Kaj}!+ diff --git a/case_mapping/uco/__pycache__/identity.cpython-39.pyc b/case_mapping/uco/__pycache__/identity.cpython-39.pyc deleted file mode 100644 index 23f8c858ab5337ea6bebf39aac3ac827a50f735d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2308 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 C_Q%-( diff --git a/case_mapping/uco/__pycache__/role.cpython-39.pyc b/case_mapping/uco/__pycache__/role.cpython-39.pyc deleted file mode 100644 index baaf4c71398de52808f4b648cbf96871ab519960..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2048 zcmbVNPmdcl6!&B%X_DO_RBbEN1M-0bLE6oU%c??MmcpqEVp|T8ka28J<1!hiwr969 z%B@Jf@d@?-5|PP#3E$sPFJ`_M@ugM_Stc@U7#i0sMmJ0e3BJPqeP8L6HQ=ArDLlj-319;i=) za_>_vl)syL?Q;R5S6by6B<@}46qyH-pq*zV4l3KDdH9S(WFCRVei?mf5_Kl_Iwo*D zfWN3qwX$xlVEsZJwFZyZ-0~Hb$_cC2xc8Wzbf?tB;0v_87Foi{3B>Vy!HVA$Y!HsP@>n*|EAZ5;sfc z^-3MnbI1tij{2pdD1!m_&;drBs4pOS1O~D}MYDJWNYG2}=+@wlr<9|E`=E`Z|Br*P ziu*QTv{!`*L0&GkSiT7XEOujJCq%3#Yf`I2?d193^mE_{(7>B6#tI$QHQcf1`NOJY zDuuEN3e#D_7b@9NWIkUv;HW3-L<{Yc{7|Y`r|1wC5wF)N^6!=1;H#nuLe+-k)&-%o2M;@UJU_S#h~i|g&A0s7;-xY^3;?G1Kka5wf{2G>bBWDHSctQ;}t zExX{>m2WW#d`FJg^8=O|shCxc&Cq`r| zsHU4a6Z=bU)zR7-yeHi3^yK_*)g9ggo&b{$zsUX4q%#2*EwHPS%>b_%*8OOm+X1`| zeS9W9fR5Z62g6|4>jlmCXY_I~s^)*gXwaDb$eE)puG8^Z+W>aJ8_@u~$BY3? zS%Twz#va23U$0b-%Sn@|P)Wj=J%AAE?mCWcU>8D%kFC22-7thF?Dg-DO2?wo*)%F6 nd{ivHvz5FYN#MQk#^xRSl_#`c4sX{1eiXpWp^^RY?GOI}Nx5`C diff --git a/case_mapping/uco/__pycache__/tool.cpython-39.pyc b/case_mapping/uco/__pycache__/tool.cpython-39.pyc deleted file mode 100644 index 476ea016cf581137ef5d966e8580e9098663ee94..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1167 zcmZuwL2uJA6tVU(cq<7nQG}8RARA>gR4N~&4Zox^&Tqm8%#nEl#-a=K6@Nn* zn67}~K3bvEAVU(Z@G4k^tB8kRqVM<3GLxwxDuV)FIx~V3SrAr$oeN{-h*_zMHSpe)X*Ti2v|tmlM_eoCITQ!$ zadt$1)PGF2#e12##CGpigj5S=m99Xzn2TIZg@*2&C=Kt371DSD0L?6!YQ<-%Fc5(# z-|2iJM`KG00mMNc;~cbbnOZUyT70;*tzArO;?DELf#a>EbD<6BuNc+UR7 zyN=S{5X`c1;IMnq(&tm9#h%-x<<`2aMqA!nXWZ%`tlB85cWBTH%Z8c3=9EoJWg#u4 zWs4fC>6~ffPL&aPmb4s%|1n9&yLFR#!cnRxYTzpjFf`4XF_d1R>$mZ-0m*U3PPAM~ z6~9pFY$6J4Vxuy9#%MRW1g3vE$O8 zF7zEZ+wQQN`(RMJjhna`1lT`+qMK!R=nDTS>_r0@APzLU%>&JCN+)mza_4s_eK%uy zHRGYnZmlI1c}}Um3r#B1a$d(7cgNg?HO@2xbmKGHMT1^cMkl~syKC?BWs6G%m_u(nz8J diff --git a/case_mapping/uco/__pycache__/types.cpython-39.pyc b/case_mapping/uco/__pycache__/types.cpython-39.pyc deleted file mode 100644 index 2f6fb43b743ba74bb633524e4323ff3925645352..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 907 zcmZ`%&2AGh5VrTHO49~7!Ub7{Lsuf%JE~Btws0ass<`BU<*p~x;Hy_oC?#)*jAZc(26O?1aqqVsDW*Ju#@~V z)GyeX&{N1*C6s07Ct!j36V&B-xP_q3TcJvj%o`@kRYUkvX;mn%YbIgb7C7+Cj2EbG zu&TU%HZ4rw*A=*z_rAs~-3=94H0#|~hxQ|5(mz$c4bjzi`x!$DQsHT=F5)dh#7UD~0ut$Udd zZdhc|aEKd&tV%HfWYy(-V&qf@f0OgmsnCm)wnNifaw Date: Wed, 21 Feb 2024 11:20:42 +0100 Subject: [PATCH 06/22] Add type checking to mix_utils/util-py module --- mix_utils/utils.py | 103 ++++++++++++++++++++------------------------- 1 file changed, 45 insertions(+), 58 deletions(-) diff --git a/mix_utils/utils.py b/mix_utils/utils.py index d0e920e..4d289e5 100644 --- a/mix_utils/utils.py +++ b/mix_utils/utils.py @@ -1,11 +1,13 @@ import re -from datetime import datetime +import datetime +from typing import Callable, Optional class CheckDuplicate(): """It aims to avoid duplication in the JSON/CASE file generated by the parsers (UFED, AXIOM etc.) """ - def check_value(self, *args, value=None, list_values=None, list_objects=None, observable_generating_f=None): + def check_value(self, *args: tuple[str, ...], value: str, list_values: list[str], + list_objects: list[dict], observable_generating_f: Callable[..., dict]) -> dict: """It checks if a specific value has been already generated related to an ObservableObject relying on the list of its values. This is meant to avoid duplication in the JSON/CASE file generated by the parsers (UFED, AXIOM etc.). @@ -34,10 +36,10 @@ def check_value(self, *args, value=None, list_values=None, list_objects=None, ob return observable_app -class AdjustText(): +class AdjustDate(): """It amends the data to either make it homogeneous or get rid of some dirty chracters extracted from the XML reports. """ - def adjust_date(self, original_date=''): + def uniform_date(self, original_date: str) -> Optional[datetime.date]: """ Amend the original date to convert it into a uniform format. The xsd:dateTime will have the format YYYY-MM-DDTHH:MM:SS(+HH:MM). @@ -66,86 +68,71 @@ def adjust_date(self, original_date=''): 'PM': '' } - uniform_date = original_date.strip() + original_date = original_date.strip() - if uniform_date == '': + if original_date == '': return None for k,v in aMonths.items(): - if uniform_date.find(k) > -1: - uniform_date = uniform_date.replace(k, v) + if original_date.find(k) > -1: + original_date = original_date.replace(k, v) break for k,v in chars_to_replace.items(): - uniform_date = uniform_date.replace(k, v) + original_date = original_date.replace(k, v) - uniform_date = uniform_date.replace(' ', 'T', 1) + original_date = original_date.replace(' ', 'T', 1) - if re.search('^[0-9]{4}', uniform_date): + if re.search('^[0-9]{4}', original_date): pass else: # when uniform_date is in Italian formato and the Year is placed before 'T' and # composed of two digits, i.e. Year=YY - uniform_date = re.sub('-([0-9][0-9])T', '-20\g<1>T', uniform_date) - uniform_date = str(uniform_date[6:10]) + uniform_date[2:6] + uniform_date[0:2] + \ - uniform_date[10:] - - #start_tz = uniform_date.find("+") - #if start_tz > -1: - # uniform_date = uniform_date[:start_tz] + original_date = re.sub('-([0-9][0-9])T', '-20\g<1>T', original_date) + original_date = str(original_date[6:10]) + original_date[2:6] + original_date[0:2] + \ + original_date[10:] + + # the code is for correctly processing the R-CSAM-E11-UFED-Samsung-Galaxy.xml report + start_tz = original_date.find("+") + if start_tz > -1: + original_date = original_date[:start_tz] + - date_chars = uniform_date[:10] # YYYY-MM-DD + date_chars = original_date[:10] # YYYY-MM-DD date_chars = date_chars.replace(".", "-") - uniform_date = date_chars + uniform_date[10:] + original_date = date_chars + original_date[10:] - #if uniform_date[-1] == '-': - # uniform_date = uniform_date[0:-1] + # as above the code is for the R-CSAM-E11-UFED-Samsung-Galaxy.xml report + if original_date[-1] == '-': + original_date = original_date[0:-1] - uniform_date = uniform_date.replace('.000', '').replace('.', ':') + original_date = original_date.replace('.000', '').replace('.', ':') - uniform_date = uniform_date.replace('.', ':') + original_date = original_date.replace('.', ':') - if re.search('T\d{2}\.', uniform_date): - uniform_date = uniform_date.replace('.', ':') + if re.search('T\d{2}\.', original_date): + original_date = original_date.replace('.', ':') - if re.search('(\d{2}:\d{2}:\d{2})$', uniform_date): + if re.search('(\d{2}:\d{2}:\d{2})$', original_date): pass else: - uniform_date = re.sub('(\d{2}:\d{2})$', '\g<1>:00', uniform_date) + original_date = re.sub('(\d{2}:\d{2})$', '\g<1>:00', original_date) - if re.search('T(\d):', uniform_date): - uniform_date = re.sub('T(\d):', 'T0\g<1>:', uniform_date) + if re.search('T(\d):', original_date): + original_date = re.sub('T(\d):', 'T0\g<1>:', original_date) - if re.search(':(\d)', uniform_date): - uniform_date = re.sub(':(\d):', ':0\g<1>', uniform_date) + if re.search(':(\d)', original_date): + original_date = re.sub(':(\d):', ':0\g<1>', original_date) - if re.search('T\d{2}:\d{2}:\d{2}(.+)$', uniform_date): - uniform_date = re.sub('(T\d{2}:\d{2}:\d{2})(.+)$', '\g<1>', uniform_date) + if re.search('T\d{2}:\d{2}:\d{2}(.+)$', original_date): + original_date = re.sub('(T\d{2}:\d{2}:\d{2})(.+)$', '\g<1>', original_date) - if uniform_date.find('+') > -1: - uniform_date = datetime.strptime(uniform_date, - '%Y-%m-%dT%H:%M:%S.%f%z') + if original_date.find('+') > -1: + uniform_date = datetime.datetime.strptime(original_date, + '%Y-%m-%dT%H:%M:%S.%f%z') else: - uniform_date = datetime.strptime(uniform_date, - '%Y-%m-%dT%H:%M:%S') + uniform_date = datetime.datetime.strptime(original_date, + '%Y-%m-%dT%H:%M:%S') return uniform_date - - def adjust_json(self, original_json=''): - """ - It gets rid of some dirty characters not allowd in the JSON values. - """ - chars_to_replace = { - '"' : '', - '\n' : '', - '\r' : '', - '\t': '', - "\\'": '', - '\\': '' - } - conform_json = original_json.strip() - - for k,v in chars_to_replace.items(): - conform_json = conform_json.replace(k, v) - - return conform_json \ No newline at end of file + \ No newline at end of file From 0a491b84476dc1f768a71d291ee36a7e2a183e1f Mon Sep 17 00:00:00 2001 From: Fabrizio Turchi Date: Wed, 21 Feb 2024 16:08:09 +0100 Subject: [PATCH 07/22] Remove AdjustDate class, useful only for UFED parser, and white space from mix_utils/utils.py module --- mix_utils/utils.py | 121 ++++----------------------------------------- 1 file changed, 9 insertions(+), 112 deletions(-) diff --git a/mix_utils/utils.py b/mix_utils/utils.py index 4d289e5..9137679 100644 --- a/mix_utils/utils.py +++ b/mix_utils/utils.py @@ -1,17 +1,15 @@ -import re -import datetime -from typing import Callable, Optional +from typing import Callable -class CheckDuplicate(): +class CheckDuplicate(): """It aims to avoid duplication in the JSON/CASE file generated by the parsers (UFED, AXIOM etc.) - """ + """ - def check_value(self, *args: tuple[str, ...], value: str, list_values: list[str], + def check_value(self, *args: tuple[str, ...], value: str, list_values: list[str], list_objects: list[dict], observable_generating_f: Callable[..., dict]) -> dict: """It checks if a specific value has been already generated related to an ObservableObject relying on the list of its values. This is meant to avoid duplication in the JSON/CASE file generated by the parsers (UFED, AXIOM etc.). - If the value is not in the list_values, a new ObservableObject is generated by using the function + If the value is not in the list_values, a new ObservableObject is generated by using the function observable_generating_f that returns, as a result, the new ObservableObject (e.g. uco-observable:ApplicationFacet, uco-observable:AccountFacet, uco-location:LatLongCoordinatesFacet: drafting:SearchedItemFacet, "uco-observable:URLFacet, uco-observable:ApplicationAccountFacet, uco-observable:DigitalAccountFacet, uco-observable:PhoneAccountFacet). @@ -25,114 +23,13 @@ def check_value(self, *args: tuple[str, ...], value: str, list_values: list[str] :param observable_generating_f: the function that will generate the corresponding kind of ObservableObject :param *args: the actual parameter of the observable_generating_f function :return: an Observableobject of a specific kind depending by the actual parameters - """ + """ if value in list_values: - idx = list_values.index(value) + idx = list_values.index(value) observable_app = list_objects[idx] - else: + else: observable_app = observable_generating_f(*args) list_values.append(value) list_objects.append(observable_app) - return observable_app - -class AdjustDate(): - """It amends the data to either make it homogeneous or get rid of some dirty chracters extracted from the XML reports. - """ - def uniform_date(self, original_date: str) -> Optional[datetime.date]: - """ - Amend the original date to convert it into a uniform format. The xsd:dateTime will have the format - YYYY-MM-DDTHH:MM:SS(+HH:MM). - """ - aMonths = { - 'Jan': '01', - 'Feb': '02', - 'Mar': '03', - 'Apr': '04', - 'May': '05', - 'Jun': '06', - 'Jul': '07', - 'Aug': '08', - 'Sep': '09', - 'Oct': '10', - 'Nov': '11', - 'Dec': '12' - } - - chars_to_replace = { - "/" : "-", - "(" : "-", - ")" : "-", - 'UTC': '', - 'AM': '', - 'PM': '' - } - - original_date = original_date.strip() - - if original_date == '': - return None - - for k,v in aMonths.items(): - if original_date.find(k) > -1: - original_date = original_date.replace(k, v) - break - for k,v in chars_to_replace.items(): - original_date = original_date.replace(k, v) - - original_date = original_date.replace(' ', 'T', 1) - - if re.search('^[0-9]{4}', original_date): - pass - else: - # when uniform_date is in Italian formato and the Year is placed before 'T' and - # composed of two digits, i.e. Year=YY - original_date = re.sub('-([0-9][0-9])T', '-20\g<1>T', original_date) - original_date = str(original_date[6:10]) + original_date[2:6] + original_date[0:2] + \ - original_date[10:] - - # the code is for correctly processing the R-CSAM-E11-UFED-Samsung-Galaxy.xml report - start_tz = original_date.find("+") - if start_tz > -1: - original_date = original_date[:start_tz] - - - date_chars = original_date[:10] # YYYY-MM-DD - date_chars = date_chars.replace(".", "-") - original_date = date_chars + original_date[10:] - - # as above the code is for the R-CSAM-E11-UFED-Samsung-Galaxy.xml report - if original_date[-1] == '-': - original_date = original_date[0:-1] - - original_date = original_date.replace('.000', '').replace('.', ':') - - original_date = original_date.replace('.', ':') - - if re.search('T\d{2}\.', original_date): - original_date = original_date.replace('.', ':') - - if re.search('(\d{2}:\d{2}:\d{2})$', original_date): - pass - else: - original_date = re.sub('(\d{2}:\d{2})$', '\g<1>:00', original_date) - - if re.search('T(\d):', original_date): - original_date = re.sub('T(\d):', 'T0\g<1>:', original_date) - - if re.search(':(\d)', original_date): - original_date = re.sub(':(\d):', ':0\g<1>', original_date) - - if re.search('T\d{2}:\d{2}:\d{2}(.+)$', original_date): - original_date = re.sub('(T\d{2}:\d{2}:\d{2})(.+)$', '\g<1>', original_date) - - - if original_date.find('+') > -1: - uniform_date = datetime.datetime.strptime(original_date, - '%Y-%m-%dT%H:%M:%S.%f%z') - else: - uniform_date = datetime.datetime.strptime(original_date, - '%Y-%m-%dT%H:%M:%S') - - return uniform_date - \ No newline at end of file + return observable_app \ No newline at end of file From ea81a4131a4b453296b7bc0bdd1cc3b0c92b67fc Mon Sep 17 00:00:00 2001 From: Fabrizio Turchi Date: Fri, 23 Feb 2024 10:22:31 +0100 Subject: [PATCH 08/22] Change structure mix_utils/utils.py module. Add test_duplicate.py for pytest --- mix_utils/test_duplicate.py | 129 ++++++++++++++++++++++++++++++++++++ mix_utils/utils.py | 59 ++++++++--------- 2 files changed, 157 insertions(+), 31 deletions(-) create mode 100644 mix_utils/test_duplicate.py diff --git a/mix_utils/test_duplicate.py b/mix_utils/test_duplicate.py new file mode 100644 index 0000000..6d00255 --- /dev/null +++ b/mix_utils/test_duplicate.py @@ -0,0 +1,129 @@ +import utils +import uuid + + +def check_app_name(app_name, app_names, app_objects, uuid): + #c_check = utils.CheckDuplicate() + observable_app = utils.check_value(app_name, uuid, + value=app_name, + list_values=app_names, + list_objects=app_objects, + observable_generating_f=generateTraceAppName) + return observable_app + +def check_geo_coordinates(latitude, longitude, geo_coordinates, geo_objects, uuid): + #c_check = utils.CheckDuplicate() + observable_app = utils.check_value(latitude, longitude, uuid, + value=str(latitude) + '#' + str(longitude), + list_values=geo_coordinates, + list_objects=geo_objects, + observable_generating_f=generateTraceLocationCoordinate) + return observable_app + +def generateTraceAppName(app_name, uuid): + observable = {"@type": "uco-observable:ApplicationFacet", + "@id":uuid, + "uco-core:name":app_name} + return observable + +def generateTraceLocationCoordinate (latitude, longitude, uuid): + observable = { + "@type": "uco-location:LatLongCoordinatesFacet", + "@id":uuid, + "uco-location:latitude": { + "@type": "xsd:decimal", + "@value": latitude + }, + "uco-location:longitude": { + "@type": "xsd:decimal", + "@value": longitude + } + } + return observable + +def test_app_name(): + app_names = list() + app_objects = list() + app_1 = 'Safari' + uuid_1 = uuid.uuid4() + check_app_name(app_1, app_names, app_objects, uuid_1) + assert app_names == [app_1] + app_2 = 'Chrome' + uuid_2 = uuid.uuid4() + check_app_name(app_2, app_names, app_objects, uuid_2) + assert app_names == [app_1, app_2] + uuid_3 = uuid.uuid4() + object_app = check_app_name(app_1, app_names, app_objects, uuid_3) + assert object_app == {"@type": "uco-observable:ApplicationFacet", + "@id":uuid_1, + "uco-core:name": app_1 + } + assert app_names == [app_1, app_2] + assert app_objects == [ + {"@type": "uco-observable:ApplicationFacet", + "@id":uuid_1, + "uco-core:name": app_1 + }, + {"@type": "uco-observable:ApplicationFacet", + "@id":uuid_2, + "uco-core:name": app_2 + } + ] + +def test_geo_coordinates(): + geo_coordinates = list() + geo_objects = list() + (lat_1, long_1, uuid_1) = (56.47267913, -71.17069244, str(uuid.uuid4())) + check_geo_coordinates(lat_1, long_1, geo_coordinates, geo_objects, uuid_1) + #print(f"\n 1) FT geo_coordinates={geo_coordinates}") + assert geo_coordinates == [str(lat_1) + '#'+ str(long_1)] + (lat_2, long_2, uuid_2) = (88.26801306, 13.21980922, str(uuid.uuid4())) + check_geo_coordinates(lat_2, long_2, geo_coordinates, geo_objects, uuid_2) + #print(f"\n 2) FT geo_coordinates={geo_coordinates}") + assert geo_coordinates == [str(lat_1) + '#' + str(long_1), str(lat_2) + '#' + str(long_2)] + uuid_3 = str(uuid.uuid4()) + uuid_4 = str(uuid.uuid4()) + geo_object = check_geo_coordinates(lat_1, long_1, geo_coordinates, geo_objects, uuid_3) + assert geo_object == { + "@type": "uco-location:LatLongCoordinatesFacet", + "@id":uuid_1, + "uco-location:latitude": { + "@type": "xsd:decimal", + "@value": lat_1 + }, + "uco-location:longitude": { + "@type": "xsd:decimal", + "@value": long_1 + } + } + #print(f"\n 3) FT geo_coordinates={geo_coordinates}") + assert geo_coordinates == [str(lat_1) + '#' + str(long_1), str(lat_2) + '#' + str(long_2)] + check_geo_coordinates(lat_2, long_2, geo_coordinates, geo_objects, uuid_4) + #print(f"\n 4) FT geo_coordinates={geo_coordinates}") + assert geo_coordinates == [str(lat_1) + '#' + str(long_1), str(lat_2) + '#' + str(long_2)] + assert geo_objects == [ + { + "@type": "uco-location:LatLongCoordinatesFacet", + "@id":uuid_1, + "uco-location:latitude": { + "@type": "xsd:decimal", + "@value": lat_1 + }, + "uco-location:longitude": { + "@type": "xsd:decimal", + "@value": long_1 + } + }, + { + "@type": "uco-location:LatLongCoordinatesFacet", + "@id":uuid_2, + "uco-location:latitude": { + "@type": "xsd:decimal", + "@value": lat_2 + }, + "uco-location:longitude": { + "@type": "xsd:decimal", + "@value": long_2 + } + } + ] \ No newline at end of file diff --git a/mix_utils/utils.py b/mix_utils/utils.py index 9137679..e507fcf 100644 --- a/mix_utils/utils.py +++ b/mix_utils/utils.py @@ -1,35 +1,32 @@ from typing import Callable -class CheckDuplicate(): - """It aims to avoid duplication in the JSON/CASE file generated by the parsers (UFED, AXIOM etc.) - """ + +def check_value(*args: tuple[str, ...], value: str, list_values: list[str], + list_objects: list[dict], observable_generating_f: Callable[..., dict]) -> dict: + """It checks if a specific value has been already generated related to an ObservableObject relying on + the list of its values. This is meant to avoid duplication in the JSON/CASE file generated by the + parsers (UFED, AXIOM etc.). + If the value is not in the list_values, a new ObservableObject is generated by using the function + observable_generating_f that returns, as a result, the new ObservableObject (e.g. uco-observable:ApplicationFacet, + uco-observable:AccountFacet, uco-location:LatLongCoordinatesFacet: drafting:SearchedItemFacet, "uco-observable:URLFacet, + uco-observable:ApplicationAccountFacet, uco-observable:DigitalAccountFacet, uco-observable:PhoneAccountFacet). - def check_value(self, *args: tuple[str, ...], value: str, list_values: list[str], - list_objects: list[dict], observable_generating_f: Callable[..., dict]) -> dict: - """It checks if a specific value has been already generated related to an ObservableObject relying on - the list of its values. This is meant to avoid duplication in the JSON/CASE file generated by the - parsers (UFED, AXIOM etc.). - If the value is not in the list_values, a new ObservableObject is generated by using the function - observable_generating_f that returns, as a result, the new ObservableObject (e.g. uco-observable:ApplicationFacet, - uco-observable:AccountFacet, uco-location:LatLongCoordinatesFacet: drafting:SearchedItemFacet, "uco-observable:URLFacet, - uco-observable:ApplicationAccountFacet, uco-observable:DigitalAccountFacet, uco-observable:PhoneAccountFacet). - - Finally the new ObservableObject is added to the list_objects (any kind of ObservableObject maintains a different list). - If the value is already in the list_values, the ObservableObject list_objects[index] is returned. + Finally the new ObservableObject is added to the list_objects (any kind of ObservableObject maintains a different list). + If the value is already in the list_values, the ObservableObject list_objects[index] is returned. + + :param value: the value to be checked within the list_values + :param list_values: the current list of values + :param list_objects: the current list of a specific kind of ObservableObject + :param observable_generating_f: the function that will generate the corresponding kind of ObservableObject + :param *args: the actual parameter of the observable_generating_f function + :return: an Observableobject of a specific kind depending by the actual parameters + """ + if value in list_values: + idx = list_values.index(value) + observable_app = list_objects[idx] + else: + observable_app = observable_generating_f(*args) + list_values.append(value) + list_objects.append(observable_app) - :param value: the value to be checked within the list_values - :param list_values: the current list of values - :param list_objects: the current list of a specific kind of ObservableObject - :param observable_generating_f: the function that will generate the corresponding kind of ObservableObject - :param *args: the actual parameter of the observable_generating_f function - :return: an Observableobject of a specific kind depending by the actual parameters - """ - if value in list_values: - idx = list_values.index(value) - observable_app = list_objects[idx] - else: - observable_app = observable_generating_f(*args) - list_values.append(value) - list_objects.append(observable_app) - - return observable_app \ No newline at end of file + return observable_app \ No newline at end of file From 1d97f406a9755bd83e5080be7da10ae6ac455c28 Mon Sep 17 00:00:00 2001 From: Alex Nelson Date: Fri, 23 Feb 2024 06:46:36 -0700 Subject: [PATCH 09/22] Enable CI on pull requests Signed-off-by: Alex Nelson --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b92454b..ce5f779 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,6 +2,7 @@ name: Continuous Integration on: push: + pull_request: jobs: build: From 6ebfcc79d63e6bd5c8a6515f786bb0bd3a0c8f36 Mon Sep 17 00:00:00 2001 From: Alex Nelson Date: Fri, 23 Feb 2024 07:57:27 -0700 Subject: [PATCH 10/22] Apply formatting Signed-off-by: Alex Nelson --- case_mapping/uco/observable.py | 39 +++--- case_mapping/uco/tool.py | 4 +- mix_utils/test_duplicate.py | 237 +++++++++++++++++---------------- mix_utils/utils.py | 17 ++- 4 files changed, 155 insertions(+), 142 deletions(-) diff --git a/case_mapping/uco/observable.py b/case_mapping/uco/observable.py index 49e2732..3d63463 100644 --- a/case_mapping/uco/observable.py +++ b/case_mapping/uco/observable.py @@ -485,8 +485,9 @@ def __init__( "uco-observable:userName": url_username, } ) - self._int_vars(**{"uco-observable:port": url_port}) - + self._int_vars(**{"uco-observable:port": url_port}) + + class FacetBrowserBookmark(FacetEntity): def __init__( self, @@ -496,7 +497,7 @@ def __init__( modifiedTime=None, createdTime=None, urlTargeted_id=None, - visitCount=None + visitCount=None, ): """ This CASEObject represents a grouping of characteristics unique to a saved shortcut that directs a @@ -511,25 +512,23 @@ def __init__( """ super().__init__() self["@type"] = "uco-observable:BrowserBookmarkFacet" - self._str_vars( - **{ - "observable:bookmarkPath": bookmarkPath - } - ) - self._int_vars( + self._str_vars(**{"observable:bookmarkPath": bookmarkPath}) + self._int_vars(**{"uco-observable:visitCount": visitCount}) + self._node_reference_vars( **{ - "uco-observable:visitCount": visitCount + "uco-observable:application": application_id, + "uco-observable:urlTargeted": urlTargeted_id, } ) - self._node_reference_vars(**{"uco-observable:application": application_id, - "uco-observable:urlTargeted": urlTargeted_id} ) self._datetime_vars( **{ "uco-observable:observableCreatedTime": accessedTime, "uco-observable:modifiedTime": modifiedTime, - "uco-observable:accessedTime": accessedTime - }) - + "uco-observable:accessedTime": accessedTime, + } + ) + + class FacetRasterPicture(FacetEntity): def __init__( self, @@ -1058,7 +1057,7 @@ def __init__( computer_name=None, created_time=None, start_time=None, - end_time=None + end_time=None, ): """ An event facet is a grouping of characteristics unique to something that happens in a digital context @@ -1082,8 +1081,12 @@ def __init__( } ) self._node_reference_vars(**{"uco-observable:cyberAction": cyber_action}) - self._datetime_vars(**{"uco-observable:startTime": start_time, - "uco-observable:endTime": end_time}) + self._datetime_vars( + **{ + "uco-observable:startTime": start_time, + "uco-observable:endTime": end_time, + } + ) class ObservableRelationship(ObjectEntity): diff --git a/case_mapping/uco/tool.py b/case_mapping/uco/tool.py index 22c2d22..52785c4 100644 --- a/case_mapping/uco/tool.py +++ b/case_mapping/uco/tool.py @@ -18,10 +18,10 @@ def __init__( **{ "uco-core:name": tool_name, "uco-tool:version": tool_version, - "uco-tool:toolType": tool_type + "uco-tool:toolType": tool_type, } ) - self._node_reference_vars(**{"uco-tool:creator": tool_creator} ) + self._node_reference_vars(**{"uco-tool:creator": tool_creator}) directory = {"uco-tool:Tool": Tool} diff --git a/mix_utils/test_duplicate.py b/mix_utils/test_duplicate.py index 6d00255..31ef921 100644 --- a/mix_utils/test_duplicate.py +++ b/mix_utils/test_duplicate.py @@ -1,129 +1,134 @@ -import utils import uuid +import utils + def check_app_name(app_name, app_names, app_objects, uuid): - #c_check = utils.CheckDuplicate() - observable_app = utils.check_value(app_name, uuid, - value=app_name, - list_values=app_names, - list_objects=app_objects, - observable_generating_f=generateTraceAppName) - return observable_app + # c_check = utils.CheckDuplicate() + observable_app = utils.check_value( + app_name, + uuid, + value=app_name, + list_values=app_names, + list_objects=app_objects, + observable_generating_f=generateTraceAppName, + ) + return observable_app + def check_geo_coordinates(latitude, longitude, geo_coordinates, geo_objects, uuid): - #c_check = utils.CheckDuplicate() - observable_app = utils.check_value(latitude, longitude, uuid, - value=str(latitude) + '#' + str(longitude), - list_values=geo_coordinates, - list_objects=geo_objects, - observable_generating_f=generateTraceLocationCoordinate) - return observable_app + # c_check = utils.CheckDuplicate() + observable_app = utils.check_value( + latitude, + longitude, + uuid, + value=str(latitude) + "#" + str(longitude), + list_values=geo_coordinates, + list_objects=geo_objects, + observable_generating_f=generateTraceLocationCoordinate, + ) + return observable_app + def generateTraceAppName(app_name, uuid): - observable = {"@type": "uco-observable:ApplicationFacet", - "@id":uuid, - "uco-core:name":app_name} - return observable + observable = { + "@type": "uco-observable:ApplicationFacet", + "@id": uuid, + "uco-core:name": app_name, + } + return observable + + +def generateTraceLocationCoordinate(latitude, longitude, uuid): + observable = { + "@type": "uco-location:LatLongCoordinatesFacet", + "@id": uuid, + "uco-location:latitude": {"@type": "xsd:decimal", "@value": latitude}, + "uco-location:longitude": {"@type": "xsd:decimal", "@value": longitude}, + } + return observable -def generateTraceLocationCoordinate (latitude, longitude, uuid): - observable = { - "@type": "uco-location:LatLongCoordinatesFacet", - "@id":uuid, - "uco-location:latitude": { - "@type": "xsd:decimal", - "@value": latitude - }, - "uco-location:longitude": { - "@type": "xsd:decimal", - "@value": longitude - } - } - return observable def test_app_name(): - app_names = list() - app_objects = list() - app_1 = 'Safari' - uuid_1 = uuid.uuid4() - check_app_name(app_1, app_names, app_objects, uuid_1) - assert app_names == [app_1] - app_2 = 'Chrome' - uuid_2 = uuid.uuid4() - check_app_name(app_2, app_names, app_objects, uuid_2) - assert app_names == [app_1, app_2] - uuid_3 = uuid.uuid4() - object_app = check_app_name(app_1, app_names, app_objects, uuid_3) - assert object_app == {"@type": "uco-observable:ApplicationFacet", - "@id":uuid_1, - "uco-core:name": app_1 - } - assert app_names == [app_1, app_2] - assert app_objects == [ - {"@type": "uco-observable:ApplicationFacet", - "@id":uuid_1, - "uco-core:name": app_1 - }, - {"@type": "uco-observable:ApplicationFacet", - "@id":uuid_2, - "uco-core:name": app_2 - } - ] + app_names = list() + app_objects = list() + app_1 = "Safari" + uuid_1 = uuid.uuid4() + check_app_name(app_1, app_names, app_objects, uuid_1) + assert app_names == [app_1] + app_2 = "Chrome" + uuid_2 = uuid.uuid4() + check_app_name(app_2, app_names, app_objects, uuid_2) + assert app_names == [app_1, app_2] + uuid_3 = uuid.uuid4() + object_app = check_app_name(app_1, app_names, app_objects, uuid_3) + assert object_app == { + "@type": "uco-observable:ApplicationFacet", + "@id": uuid_1, + "uco-core:name": app_1, + } + assert app_names == [app_1, app_2] + assert app_objects == [ + { + "@type": "uco-observable:ApplicationFacet", + "@id": uuid_1, + "uco-core:name": app_1, + }, + { + "@type": "uco-observable:ApplicationFacet", + "@id": uuid_2, + "uco-core:name": app_2, + }, + ] + def test_geo_coordinates(): - geo_coordinates = list() - geo_objects = list() - (lat_1, long_1, uuid_1) = (56.47267913, -71.17069244, str(uuid.uuid4())) - check_geo_coordinates(lat_1, long_1, geo_coordinates, geo_objects, uuid_1) - #print(f"\n 1) FT geo_coordinates={geo_coordinates}") - assert geo_coordinates == [str(lat_1) + '#'+ str(long_1)] - (lat_2, long_2, uuid_2) = (88.26801306, 13.21980922, str(uuid.uuid4())) - check_geo_coordinates(lat_2, long_2, geo_coordinates, geo_objects, uuid_2) - #print(f"\n 2) FT geo_coordinates={geo_coordinates}") - assert geo_coordinates == [str(lat_1) + '#' + str(long_1), str(lat_2) + '#' + str(long_2)] - uuid_3 = str(uuid.uuid4()) - uuid_4 = str(uuid.uuid4()) - geo_object = check_geo_coordinates(lat_1, long_1, geo_coordinates, geo_objects, uuid_3) - assert geo_object == { - "@type": "uco-location:LatLongCoordinatesFacet", - "@id":uuid_1, - "uco-location:latitude": { - "@type": "xsd:decimal", - "@value": lat_1 - }, - "uco-location:longitude": { - "@type": "xsd:decimal", - "@value": long_1 - } - } - #print(f"\n 3) FT geo_coordinates={geo_coordinates}") - assert geo_coordinates == [str(lat_1) + '#' + str(long_1), str(lat_2) + '#' + str(long_2)] - check_geo_coordinates(lat_2, long_2, geo_coordinates, geo_objects, uuid_4) - #print(f"\n 4) FT geo_coordinates={geo_coordinates}") - assert geo_coordinates == [str(lat_1) + '#' + str(long_1), str(lat_2) + '#' + str(long_2)] - assert geo_objects == [ - { - "@type": "uco-location:LatLongCoordinatesFacet", - "@id":uuid_1, - "uco-location:latitude": { - "@type": "xsd:decimal", - "@value": lat_1 - }, - "uco-location:longitude": { - "@type": "xsd:decimal", - "@value": long_1 - } - }, - { - "@type": "uco-location:LatLongCoordinatesFacet", - "@id":uuid_2, - "uco-location:latitude": { - "@type": "xsd:decimal", - "@value": lat_2 - }, - "uco-location:longitude": { - "@type": "xsd:decimal", - "@value": long_2 - } - } - ] \ No newline at end of file + geo_coordinates = list() + geo_objects = list() + (lat_1, long_1, uuid_1) = (56.47267913, -71.17069244, str(uuid.uuid4())) + check_geo_coordinates(lat_1, long_1, geo_coordinates, geo_objects, uuid_1) + # print(f"\n 1) FT geo_coordinates={geo_coordinates}") + assert geo_coordinates == [str(lat_1) + "#" + str(long_1)] + (lat_2, long_2, uuid_2) = (88.26801306, 13.21980922, str(uuid.uuid4())) + check_geo_coordinates(lat_2, long_2, geo_coordinates, geo_objects, uuid_2) + # print(f"\n 2) FT geo_coordinates={geo_coordinates}") + assert geo_coordinates == [ + str(lat_1) + "#" + str(long_1), + str(lat_2) + "#" + str(long_2), + ] + uuid_3 = str(uuid.uuid4()) + uuid_4 = str(uuid.uuid4()) + geo_object = check_geo_coordinates( + lat_1, long_1, geo_coordinates, geo_objects, uuid_3 + ) + assert geo_object == { + "@type": "uco-location:LatLongCoordinatesFacet", + "@id": uuid_1, + "uco-location:latitude": {"@type": "xsd:decimal", "@value": lat_1}, + "uco-location:longitude": {"@type": "xsd:decimal", "@value": long_1}, + } + # print(f"\n 3) FT geo_coordinates={geo_coordinates}") + assert geo_coordinates == [ + str(lat_1) + "#" + str(long_1), + str(lat_2) + "#" + str(long_2), + ] + check_geo_coordinates(lat_2, long_2, geo_coordinates, geo_objects, uuid_4) + # print(f"\n 4) FT geo_coordinates={geo_coordinates}") + assert geo_coordinates == [ + str(lat_1) + "#" + str(long_1), + str(lat_2) + "#" + str(long_2), + ] + assert geo_objects == [ + { + "@type": "uco-location:LatLongCoordinatesFacet", + "@id": uuid_1, + "uco-location:latitude": {"@type": "xsd:decimal", "@value": lat_1}, + "uco-location:longitude": {"@type": "xsd:decimal", "@value": long_1}, + }, + { + "@type": "uco-location:LatLongCoordinatesFacet", + "@id": uuid_2, + "uco-location:latitude": {"@type": "xsd:decimal", "@value": lat_2}, + "uco-location:longitude": {"@type": "xsd:decimal", "@value": long_2}, + }, + ] diff --git a/mix_utils/utils.py b/mix_utils/utils.py index e507fcf..455efd8 100644 --- a/mix_utils/utils.py +++ b/mix_utils/utils.py @@ -1,8 +1,13 @@ from typing import Callable -def check_value(*args: tuple[str, ...], value: str, list_values: list[str], - list_objects: list[dict], observable_generating_f: Callable[..., dict]) -> dict: +def check_value( + *args: tuple[str, ...], + value: str, + list_values: list[str], + list_objects: list[dict], + observable_generating_f: Callable[..., dict] +) -> dict: """It checks if a specific value has been already generated related to an ObservableObject relying on the list of its values. This is meant to avoid duplication in the JSON/CASE file generated by the parsers (UFED, AXIOM etc.). @@ -10,10 +15,10 @@ def check_value(*args: tuple[str, ...], value: str, list_values: list[str], observable_generating_f that returns, as a result, the new ObservableObject (e.g. uco-observable:ApplicationFacet, uco-observable:AccountFacet, uco-location:LatLongCoordinatesFacet: drafting:SearchedItemFacet, "uco-observable:URLFacet, uco-observable:ApplicationAccountFacet, uco-observable:DigitalAccountFacet, uco-observable:PhoneAccountFacet). - + Finally the new ObservableObject is added to the list_objects (any kind of ObservableObject maintains a different list). If the value is already in the list_values, the ObservableObject list_objects[index] is returned. - + :param value: the value to be checked within the list_values :param list_values: the current list of values :param list_objects: the current list of a specific kind of ObservableObject @@ -28,5 +33,5 @@ def check_value(*args: tuple[str, ...], value: str, list_values: list[str], observable_app = observable_generating_f(*args) list_values.append(value) list_objects.append(observable_app) - - return observable_app \ No newline at end of file + + return observable_app From 169c32b8ebdbf337b69193931207628b2e070566 Mon Sep 17 00:00:00 2001 From: Fabrizio Turchi Date: Mon, 26 Feb 2024 14:35:00 +0100 Subject: [PATCH 11/22] Fix #12 issue, changing FacetUrlHistory and UrlHistoryEntry classes --- case_mapping/uco/observable.py | 91 +++++++++++++++++----------------- mix_utils/test_duplicate.py | 14 +++--- 2 files changed, 53 insertions(+), 52 deletions(-) diff --git a/case_mapping/uco/observable.py b/case_mapping/uco/observable.py index 3d63463..78e2626 100644 --- a/case_mapping/uco/observable.py +++ b/case_mapping/uco/observable.py @@ -373,78 +373,79 @@ def __init__(self, has_changed=None, state=None, facets=None): class FacetUrlHistory(FacetEntity): - def __init__(self, browser_info, history_entries=None): + def __init__(self, browser=None, history_entries=None): """ :param browser_info: An observable object containing a URLHistoryFacet :param history_entries: A list of URLHistoryEntry types """ - super().__init__() self["@type"] = "uco-observable:URLHistoryFacet" - self._node_reference_vars(**{"uco-observable:browserInformation": browser_info}) - self.append_history_entries(history_entries) - - @unpack_args_array - def append_history_entries(self, *args): - """ - Used to add history entries to this URL History facet - :param args: A single/tuple of URLHistoryEntry class types - """ - self._append_observable_objects("uco-observable:urlHistoryEntry", *args) + self._node_reference_vars( + **{ + 'uco-observable:browserInformation': browser, + 'uco-observable:urlHistoryEntry': history_entries, + } + ) class UrlHistoryEntry(FacetEntity): - def __init__( - self, + def __init__(self, + browser_user_profile=None, + expiration_time=None, first_visit=None, + host_name=None, + keyword_search_term=None, last_visit=None, - expiration_time=None, manually_entered_count=None, - url=None, - user_profile=None, page_title=None, referrer_url=None, + url=None, visit_count=None, - keyword_search_term=None, - allocation_status=None, - ): + ): + """ - :param first_visit: - :param last_visit: - :param expiration_time: - :param manually_entered_count: - :param url: An observable object with a URLFacet - :param user_profile: - :param page_title: - :param referrer_url: - :param visit_count: - :param keyword_search_term: - :param allocation_status: + :param browser_user_profile: The web browser user profile for which the URL history entry was created. + :param expiration_time: The date and time at which the validity of the object expires. + :param first_visit: The date/time that the URL referred to by the URL field was first visited. + :param host_name: The hostname of the system. + :param keyword_search_term: The string representing a keyword search term contained within the URL field. + :param last_visit: The date/time that the URL referred to by the URL field was last visited. + :param manually_entered_count: The number of times the URL referred to by the URL field was manually entered into the browser's address field by the user. + :param page_title: The title of a web page + :param referrer_url: The origination point (i.e., URL) of a URL request. + :param url: An observable object with a URLFacet. + :param visit_count: The number of times a URL has been visited by a particular web browser. """ super().__init__() self["@type"] = "uco-observable:URLHistoryEntry" self._str_vars( **{ - "uco-observable:userProfile": user_profile, # todo: referral? - "uco-observable:pageTitle": page_title, - "uco-observable:referrerUrl": referrer_url, - "uco-observable:keywordSearchTerm": keyword_search_term, - "uco-observable:allocationStatus": allocation_status, - } + "uco-observable:browserUserProfile": browser_user_profile, + "uco-observable:hostname": host_name, + "uco-observable:pageTitle": page_title, + "uco-observable:keywordSearchTerm": keyword_search_term, + } + ) + self._int_vars( + **{ + "uco-observable:visitCount": visit_count, + "uco-observable:manuallyEnteredCount": manually_entered_count, + } ) - self._int_vars(**{"uco-observable:visitCount": visit_count}) self._datetime_vars( **{ - "uco-observable:firstVisit": first_visit, - "uco-observable:lastVisit": last_visit, - "uco-observable:expirationTime": expiration_time, - } + "uco-observable:firstVisit": first_visit, + "uco-observable:lastVisit": last_visit, + "uco-observable:expirationTime": expiration_time, + } ) - self._nonegative_int_vars( - **{"uco-observable:manuallyEnteredCount": manually_entered_count} + self._node_reference_vars( + **{ + "uco-observable:ble:referrerUrl": referrer_url, + "uco-observable:url": url, + } ) - self._node_reference_vars(**{"uco-observable:url": url}) class FacetUrl(FacetEntity): diff --git a/mix_utils/test_duplicate.py b/mix_utils/test_duplicate.py index 31ef921..d16764d 100644 --- a/mix_utils/test_duplicate.py +++ b/mix_utils/test_duplicate.py @@ -53,14 +53,14 @@ def test_app_name(): app_names = list() app_objects = list() app_1 = "Safari" - uuid_1 = uuid.uuid4() + uuid_1 = "kb:" + uuid.uuid4() check_app_name(app_1, app_names, app_objects, uuid_1) assert app_names == [app_1] app_2 = "Chrome" - uuid_2 = uuid.uuid4() + uuid_2 = "kb:" + uuid.uuid4() check_app_name(app_2, app_names, app_objects, uuid_2) assert app_names == [app_1, app_2] - uuid_3 = uuid.uuid4() + uuid_3 = "kb:" + uuid.uuid4() object_app = check_app_name(app_1, app_names, app_objects, uuid_3) assert object_app == { "@type": "uco-observable:ApplicationFacet", @@ -85,19 +85,19 @@ def test_app_name(): def test_geo_coordinates(): geo_coordinates = list() geo_objects = list() - (lat_1, long_1, uuid_1) = (56.47267913, -71.17069244, str(uuid.uuid4())) + (lat_1, long_1, uuid_1) = (56.47267913, -71.17069244, "kb:" + str(uuid.uuid4())) check_geo_coordinates(lat_1, long_1, geo_coordinates, geo_objects, uuid_1) # print(f"\n 1) FT geo_coordinates={geo_coordinates}") assert geo_coordinates == [str(lat_1) + "#" + str(long_1)] - (lat_2, long_2, uuid_2) = (88.26801306, 13.21980922, str(uuid.uuid4())) + (lat_2, long_2, uuid_2) = (88.26801306, 13.21980922, "kb:" + str(uuid.uuid4())) check_geo_coordinates(lat_2, long_2, geo_coordinates, geo_objects, uuid_2) # print(f"\n 2) FT geo_coordinates={geo_coordinates}") assert geo_coordinates == [ str(lat_1) + "#" + str(long_1), str(lat_2) + "#" + str(long_2), ] - uuid_3 = str(uuid.uuid4()) - uuid_4 = str(uuid.uuid4()) + uuid_3 = "kb:" + str(uuid.uuid4()) + uuid_4 = "kb:" + str(uuid.uuid4()) geo_object = check_geo_coordinates( lat_1, long_1, geo_coordinates, geo_objects, uuid_3 ) From df8c25e7eadac6aee3d68b20f45366fb82741b3b Mon Sep 17 00:00:00 2001 From: Fabrizio Turchi Date: Tue, 27 Feb 2024 15:21:41 +0100 Subject: [PATCH 12/22] Reformatted example.py --- example.py | 42 +++++++++++++++++++++++++++++++++++++ mix_utils/test_duplicate.py | 6 +++--- 2 files changed, 45 insertions(+), 3 deletions(-) diff --git a/example.py b/example.py index 24eea7a..149bf66 100644 --- a/example.py +++ b/example.py @@ -159,6 +159,48 @@ def _next_timestamp() -> datetime: bundle.append_to_uco_object(cyber_item3) +################################################### +# Adding an FacetUrlHistory and aUrlHistoryEntry # +################################################### +url_object = uco.observable.ObservableObject() +url_facet = uco.observable.FacetUrl(url_address="www.docker.com/howto") +url_object.append_facets(url_facet) +bundle.append_to_uco_object(url_object) + +url_date_expiration = datetime.strptime("2024-12-27T14:55:01", "%Y-%m-%dT%H:%M:%S") +url_date_first = datetime.strptime("2024-01-02T15:55:01", "%Y-%m-%dT%H:%M:%S") +url_date_last = datetime.strptime("2024-02-10T10:55:01", "%Y-%m-%dT%H:%M:%S") + +url_history_entry_object = uco.observable.ObservableObject() +url_history_entry = uco.observable.UrlHistoryEntry( + browser_user_profile="Jill", + expiration_time=url_date_expiration, + first_visit=url_date_first, + host_name="case_test", + keyword_search_term="docker", + last_visit=url_date_last, + manually_entered_count=10, + page_title="Docker tutorial", + referrer_url=url_object, + url=url_object, + visit_count=18, +) +url_history_entry_object.append_facets(url_history_entry) +bundle.append_to_uco_object(url_history_entry_object) + +browser_object = uco.observable.ObservableObject() +browser_facet = uco.observable.FacetApplication(app_name="Safari") +browser_object.append_facets(browser_facet) +bundle.append_to_uco_object(browser_object) + +url_history_object = uco.observable.ObservableObject() +url_history_facet = uco.observable.FacetUrlHistory( + browser=browser_object, history_entries=[url_history_entry_object] +) +url_history_object.append_facets(url_history_facet) +bundle.append_to_uco_object(url_history_object) + + ############################ # Adding an SMS Account # ############################ diff --git a/mix_utils/test_duplicate.py b/mix_utils/test_duplicate.py index d16764d..dc35f31 100644 --- a/mix_utils/test_duplicate.py +++ b/mix_utils/test_duplicate.py @@ -53,14 +53,14 @@ def test_app_name(): app_names = list() app_objects = list() app_1 = "Safari" - uuid_1 = "kb:" + uuid.uuid4() + uuid_1 = "kb:" + str(uuid.uuid4()) check_app_name(app_1, app_names, app_objects, uuid_1) assert app_names == [app_1] app_2 = "Chrome" - uuid_2 = "kb:" + uuid.uuid4() + uuid_2 = "kb:" + str(uuid.uuid4()) check_app_name(app_2, app_names, app_objects, uuid_2) assert app_names == [app_1, app_2] - uuid_3 = "kb:" + uuid.uuid4() + uuid_3 = "kb:" + str(uuid.uuid4()) object_app = check_app_name(app_1, app_names, app_objects, uuid_3) assert object_app == { "@type": "uco-observable:ApplicationFacet", From e888d6ff6fea6d44f63087fc9115e377fa7409a0 Mon Sep 17 00:00:00 2001 From: Alex Nelson Date: Wed, 28 Feb 2024 08:53:08 -0500 Subject: [PATCH 13/22] Apply formatting Signed-off-by: Alex Nelson --- case_mapping/uco/observable.py | 50 +++++++++++++++++----------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/case_mapping/uco/observable.py b/case_mapping/uco/observable.py index 78e2626..3ffc430 100644 --- a/case_mapping/uco/observable.py +++ b/case_mapping/uco/observable.py @@ -382,15 +382,16 @@ def __init__(self, browser=None, history_entries=None): self["@type"] = "uco-observable:URLHistoryFacet" self._node_reference_vars( **{ - 'uco-observable:browserInformation': browser, - 'uco-observable:urlHistoryEntry': history_entries, + "uco-observable:browserInformation": browser, + "uco-observable:urlHistoryEntry": history_entries, } ) class UrlHistoryEntry(FacetEntity): - def __init__(self, - browser_user_profile=None, + def __init__( + self, + browser_user_profile=None, expiration_time=None, first_visit=None, host_name=None, @@ -399,19 +400,18 @@ def __init__(self, manually_entered_count=None, page_title=None, referrer_url=None, - url=None, + url=None, visit_count=None, - ): - + ): """ :param browser_user_profile: The web browser user profile for which the URL history entry was created. :param expiration_time: The date and time at which the validity of the object expires. :param first_visit: The date/time that the URL referred to by the URL field was first visited. :param host_name: The hostname of the system. :param keyword_search_term: The string representing a keyword search term contained within the URL field. - :param last_visit: The date/time that the URL referred to by the URL field was last visited. - :param manually_entered_count: The number of times the URL referred to by the URL field was manually entered into the browser's address field by the user. - :param page_title: The title of a web page + :param last_visit: The date/time that the URL referred to by the URL field was last visited. + :param manually_entered_count: The number of times the URL referred to by the URL field was manually entered into the browser's address field by the user. + :param page_title: The title of a web page :param referrer_url: The origination point (i.e., URL) of a URL request. :param url: An observable object with a URLFacet. :param visit_count: The number of times a URL has been visited by a particular web browser. @@ -421,30 +421,30 @@ def __init__(self, self["@type"] = "uco-observable:URLHistoryEntry" self._str_vars( **{ - "uco-observable:browserUserProfile": browser_user_profile, - "uco-observable:hostname": host_name, - "uco-observable:pageTitle": page_title, - "uco-observable:keywordSearchTerm": keyword_search_term, - } + "uco-observable:browserUserProfile": browser_user_profile, + "uco-observable:hostname": host_name, + "uco-observable:pageTitle": page_title, + "uco-observable:keywordSearchTerm": keyword_search_term, + } ) self._int_vars( **{ - "uco-observable:visitCount": visit_count, - "uco-observable:manuallyEnteredCount": manually_entered_count, - } + "uco-observable:visitCount": visit_count, + "uco-observable:manuallyEnteredCount": manually_entered_count, + } ) self._datetime_vars( **{ - "uco-observable:firstVisit": first_visit, - "uco-observable:lastVisit": last_visit, - "uco-observable:expirationTime": expiration_time, - } + "uco-observable:firstVisit": first_visit, + "uco-observable:lastVisit": last_visit, + "uco-observable:expirationTime": expiration_time, + } ) self._node_reference_vars( **{ - "uco-observable:ble:referrerUrl": referrer_url, - "uco-observable:url": url, - } + "uco-observable:ble:referrerUrl": referrer_url, + "uco-observable:url": url, + } ) From 05d7684adcc4424a77e2868219217dcda94d437c Mon Sep 17 00:00:00 2001 From: Alex Nelson Date: Wed, 28 Feb 2024 08:54:32 -0500 Subject: [PATCH 14/22] Deactivate validation report Reported-by: Keith Chason Signed-off-by: Alex Nelson --- .github/workflows/ci.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ce5f779..a2b4da1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -43,10 +43,6 @@ jobs: case-path: ./ case-version: "case-1.3.0" extension-filter: "jsonld" - report-in-pr: "true" - github-token: ${{ secrets.GITHUB_TOKEN }} - repository: ${{ github.repository }} - pull-request: ${{ github.event.pull_request.number }} # Always build the package as a sanity check to ensure no issues with the build system # exist as part of the CI process From 26a516f27eea276c64a0525bc90266c251658462 Mon Sep 17 00:00:00 2001 From: Alex Nelson Date: Wed, 28 Feb 2024 09:31:38 -0500 Subject: [PATCH 15/22] Fix data typing Signed-off-by: Alex Nelson --- case_mapping/uco/observable.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/case_mapping/uco/observable.py b/case_mapping/uco/observable.py index 3ffc430..b72b9c7 100644 --- a/case_mapping/uco/observable.py +++ b/case_mapping/uco/observable.py @@ -427,12 +427,7 @@ def __init__( "uco-observable:keywordSearchTerm": keyword_search_term, } ) - self._int_vars( - **{ - "uco-observable:visitCount": visit_count, - "uco-observable:manuallyEnteredCount": manually_entered_count, - } - ) + self._int_vars(**{"uco-observable:visitCount": visit_count}) self._datetime_vars( **{ "uco-observable:firstVisit": first_visit, @@ -446,6 +441,14 @@ def __init__( "uco-observable:url": url, } ) + # TODO AJN: This is one instance of xsd:nonNegativeInteger. + # I'm uncertain at the moment if there are other instances in + # the ontology requiring nonNegativeIntegers; if so, the + # FacetEntity class needs to have a helper function added. + self["uco-observable:manuallyEnteredCount"] = { + "@type": "xsd:nonNegativeInteger", + "@value": "%d" % manually_entered_count, + } class FacetUrl(FacetEntity): From 2d48c7fac9621807a1cd52a950d52b1a367d6324 Mon Sep 17 00:00:00 2001 From: Fabrizio Turchi Date: Thu, 29 Feb 2024 09:42:28 +0100 Subject: [PATCH 16/22] Reformatted line by Flake by CI action --- case.jsonld | 265 ++++++++++++++++++++++----------- case_mapping/uco/observable.py | 211 ++++++++++++++++++-------- example.py | 72 +++++---- 3 files changed, 375 insertions(+), 173 deletions(-) diff --git a/case.jsonld b/case.jsonld index d37a918..a126f70 100644 --- a/case.jsonld +++ b/case.jsonld @@ -1,5 +1,5 @@ { - "@id": "aef8e4c4-db83-59fd-8f71-b65cd7676c0a", + "@id": "0caf82e9-c20e-447d-9b3d-8db30216847f", "@context": { "@vocab": "http://caseontology.org/core#", "case-investigation": "https://ontology.caseontology.org/case/investigation/", @@ -21,30 +21,30 @@ "uco-core:description": "An Example Case File", "uco-core:object": [ { - "@id": "307df50e-0116-5c51-904c-d5aa69279018", + "@id": "b41b368f-1ca3-49c1-94f1-7cfb30a6322c", "@type": "uco-identity:Organization", "uco-core:name": "Nikon" }, { - "@id": "1ccd06e1-1a39-53c2-8e70-86ad7c48ec5c", + "@id": "9f43661d-d362-4867-b6a4-91940fffacbf", "@type": "uco-observable:ObservableObject", "uco-core:hasFacet": [ { - "@id": "e26d7f7d-081c-58e5-8990-77ec13d60d89", + "@id": "0073f0da-01e3-40a4-aeba-b007f53f7e2b", "@type": "uco-observable:DeviceFacet", "uco-observable:manufacturer": { - "@id": "307df50e-0116-5c51-904c-d5aa69279018" + "@id": "b41b368f-1ca3-49c1-94f1-7cfb30a6322c" }, "uco-observable:model": "D750" } ] }, { - "@id": "fa07818e-5832-5d5f-81b4-f7e143e98252", + "@id": "d217a13f-4e45-4761-9ce3-75b4bb96245d", "@type": "uco-observable:ObservableObject", "uco-core:hasFacet": [ { - "@id": "9ab260ac-cdf5-5524-9047-3ea85ebfb6e2", + "@id": "ed8e9074-075a-4164-adf7-e6ad07aa3554", "@type": "uco-observable:FileFacet", "uco-observable:fileSystemType": "EXT4", "uco-observable:fileName": "IMG_0123.jpg", @@ -56,7 +56,7 @@ } }, { - "@id": "09cab366-d82b-57f8-a94c-9814bf7ef63d", + "@id": "82b261be-f5eb-4958-b6eb-80032e45577d", "@type": "uco-observable:ContentDataFacet", "uco-observable:magicNumber": "/9j/ww==", "uco-observable:mimeType": "image/jpg", @@ -71,7 +71,7 @@ }, "uco-observable:hash": [ { - "@id": "84c0e1ca-a4da-59f3-b60a-096983862089", + "@id": "a63a7b6d-897c-409d-871a-7cae45888fcd", "@type": "uco-types:Hash", "uco-types:hashMethod": { "@type": "uco-vocabulary:HashNameVocab", @@ -85,7 +85,7 @@ ] }, { - "@id": "f3ce4eee-0a5d-54a7-9cc7-5ac043234d77", + "@id": "82425d96-24df-407d-b850-a59664d52973", "@type": "uco-observable:RasterPictureFacet", "uco-observable:pictureType": "jpg", "uco-observable:pictureHeight": { @@ -102,20 +102,20 @@ } }, { - "@id": "d6585cd5-ad4b-54c3-a649-d45d12d1605d", + "@id": "97755a73-f374-45d9-b8d5-7e5768715afc", "@type": "uco-observable:EXIFFacet", "uco-observable:exifData": { - "@id": "ab4cd39a-dea8-558b-a49e-4aedd9aff7f1", + "@id": "03ac244a-c031-4c01-abea-3efc8e6c5eab", "@type": "uco-types:ControlledDictionary", "uco-types:entry": [ { - "@id": "7721bd93-fff7-5e1d-a14e-2d627c399ebe", + "@id": "946fee94-2e9c-4219-8c29-793ff26a526e", "@type": "uco-types:ControlledDictionaryEntry", "uco-types:key": "Make", "uco-types:value": "Canon" }, { - "@id": "9ed3e5de-cde8-5cec-a71b-774e485da67d", + "@id": "85f1a4b2-b71e-430a-aec9-6540e05a7366", "@type": "uco-types:ControlledDictionaryEntry", "uco-types:key": "Model", "uco-types:value": "Powershot" @@ -126,12 +126,12 @@ ] }, { - "@id": "2ed228fb-6c23-567a-8128-c891ccdb0dc6", + "@id": "8e18c77d-73e3-4569-b1fb-a0af403da57a", "@type": "uco-identity:Organization", "uco-core:name": "Apple" }, { - "@id": "268ab394-417e-579a-871b-84947adc9926", + "@id": "f995e449-4fb5-484b-a99a-b63343e7702f", "@type": "case-investigation:InvestigativeAction", "uco-core:name": "annotated", "uco-action:startTime": { @@ -144,10 +144,10 @@ }, "uco-core:hasFacet": [ { - "@id": "102c218a-bc39-52e6-8228-abbc200378cf", + "@id": "5016338f-a862-476c-bbb7-18b33367c91f", "@type": "uco-observable:DeviceFacet", "uco-observable:manufacturer": { - "@id": "2ed228fb-6c23-567a-8128-c891ccdb0dc6" + "@id": "8e18c77d-73e3-4569-b1fb-a0af403da57a" }, "uco-observable:deviceType": "iPhone", "uco-observable:model": "6XS", @@ -156,12 +156,12 @@ ] }, { - "@id": "f3f8c0fb-6a78-5547-aa4f-7c995d9a8732", + "@id": "09d797d5-15fe-4549-9e3e-5cf6c5e2cb33", "@type": "uco-identity:Organization", "uco-core:name": "oneplus" }, { - "@id": "19ff5a9d-1d15-5a6f-858d-28528d5f90d9", + "@id": "8baf2e57-1378-4693-9e0c-15ef0175eacb", "@type": "case-investigation:InvestigativeAction", "uco-core:name": "annotated", "uco-action:startTime": { @@ -174,10 +174,10 @@ }, "uco-core:hasFacet": [ { - "@id": "713d0464-a90c-52f3-88f4-1f0a4a9de091", + "@id": "6f1ae468-7a6d-46cd-b6c0-22f8081bf578", "@type": "uco-observable:DeviceFacet", "uco-observable:manufacturer": { - "@id": "f3f8c0fb-6a78-5547-aa4f-7c995d9a8732" + "@id": "09d797d5-15fe-4549-9e3e-5cf6c5e2cb33" }, "uco-observable:deviceType": "Android", "uco-observable:model": "8", @@ -186,7 +186,7 @@ ] }, { - "@id": "56004659-8649-5ef2-b2e9-e1dae56877f0", + "@id": "641a0e7a-b745-416a-9b6f-ca44b71973eb", "@type": "uco-observable:ObservableRelationship", "uco-core:isDirectional": { "@type": "xsd:boolean", @@ -194,38 +194,38 @@ }, "uco-core:kindOfRelationship": "Contained_Within", "uco-core:source": { - "@id": "1ccd06e1-1a39-53c2-8e70-86ad7c48ec5c" + "@id": "9f43661d-d362-4867-b6a4-91940fffacbf" }, "uco-core:target": { - "@id": "fa07818e-5832-5d5f-81b4-f7e143e98252" + "@id": "d217a13f-4e45-4761-9ce3-75b4bb96245d" }, "uco-core:hasFacet": [ { - "@id": "862e79c2-ecab-5798-8c43-09ac916ab0d4", + "@id": "a2938dcc-188c-4843-ba6a-a8bbcbc4ebe5", "@type": "uco-observable:PathRelationFacet", "uco-observable:path": "/sdcard/IMG_0123.jpg" } ] }, { - "@id": "72930cf3-7693-5c80-ae79-ca82151b5d30", + "@id": "fa946986-7505-4ed3-8ec9-d90dab185779", "@type": "uco-observable:ObservableObject", "uco-core:hasFacet": [ { - "@id": "5a33cc98-bf34-5011-82ee-1e664da00d29", + "@id": "22147750-7302-4358-8a7b-c83dd73d854c", "@type": "uco-observable:EmailAccountFacet", "uco-observable:emailAddress": { - "@id": "94adcf50-cad5-5a2d-9be3-e1e42d698028" + "@id": "086c1ade-7aea-476d-81d3-d34dd24ddb9b" } } ] }, { - "@id": "94adcf50-cad5-5a2d-9be3-e1e42d698028", + "@id": "086c1ade-7aea-476d-81d3-d34dd24ddb9b", "@type": "uco-observable:ObservableObject", "uco-core:hasFacet": [ { - "@id": "069043f2-89ee-532e-892b-d90a55ed987c", + "@id": "3fc634a8-10cc-45b1-bf23-bdfcf78773a6", "@type": "uco-observable:EmailAddressFacet", "uco-observable:addressValue": "info@example.com", "uco-observable:displayName": "Example User" @@ -233,24 +233,24 @@ ] }, { - "@id": "742fedd8-ec56-5efd-bbae-eac6de464215", + "@id": "e50a346b-d97a-4e83-8249-424a643126d6", "@type": "uco-observable:ObservableObject", "uco-core:hasFacet": [ { - "@id": "1e2a360e-9b34-5fa9-978d-e28c455e59d0", + "@id": "0aa38f1b-2500-46f2-8f7a-b60054c18ded", "@type": "uco-observable:EmailAccountFacet", "uco-observable:emailAddress": { - "@id": "a3f00ca2-db54-51a9-a281-1125bbd37783" + "@id": "8961e3cc-477e-4d2e-bee3-365289117397" } } ] }, { - "@id": "a3f00ca2-db54-51a9-a281-1125bbd37783", + "@id": "8961e3cc-477e-4d2e-bee3-365289117397", "@type": "uco-observable:ObservableObject", "uco-core:hasFacet": [ { - "@id": "f72553c7-7fa7-5354-aa68-7837e9e7b8c4", + "@id": "c82fc976-be37-4031-8b3e-c22692a4f929", "@type": "uco-observable:EmailAddressFacet", "uco-observable:addressValue": "admin@example.com", "uco-observable:displayName": "Example Admin" @@ -258,11 +258,11 @@ ] }, { - "@id": "70e47458-fd7d-594d-816e-272d5bb1e440", + "@id": "fbe0f0d4-9b00-46bb-a1f5-fa4a5fb87630", "@type": "uco-observable:ObservableObject", "uco-core:hasFacet": [ { - "@id": "60d29bf6-1cb0-5974-866a-2f1d60ee4a51", + "@id": "8c2aa1a8-dd71-4cc7-9a02-0a41caeffb52", "@type": "uco-observable:EmailMessageFacet", "uco-observable:subject": "Thoughts on Our Next Book Club Pick?", "uco-observable:body": "Hello fellow bookworms! It's that time again.", @@ -276,47 +276,146 @@ "@value": "2023-01-01T01:06:06.000006+00:00" }, "uco-observable:from": { - "@id": "94adcf50-cad5-5a2d-9be3-e1e42d698028" + "@id": "086c1ade-7aea-476d-81d3-d34dd24ddb9b" }, "uco-observable:to": [ { - "@id": "94adcf50-cad5-5a2d-9be3-e1e42d698028" + "@id": "086c1ade-7aea-476d-81d3-d34dd24ddb9b" }, { - "@id": "a3f00ca2-db54-51a9-a281-1125bbd37783" + "@id": "8961e3cc-477e-4d2e-bee3-365289117397" } ] } ] }, { - "@id": "41bc2540-fdd8-5753-864c-00a5de7d0d2e", + "@id": "2768d96c-cbc0-437f-bae8-6341427410cf", "@type": "uco-observable:ObservableObject", "uco-core:hasFacet": [ { - "@id": "0f0b71e7-39a8-5249-83a5-b52888d5c8b7", + "@id": "62492c88-8511-4567-a2ff-7df81ae89b83", + "@type": "uco-observable:URLFacet", + "uco-observable:fullValue": "www.docker.com/howto" + } + ] + }, + { + "@id": "2382da9b-1038-4c8c-bdd6-723d8ec0cc78", + "@type": "uco-observable:ObservableObject", + "uco-core:hasFacet": [ + { + "@id": "2f7fb681-6af0-45d0-a618-4bac4407cdc8", + "@type": "uco-observable:ApplicationFacet", + "uco-core:name": "Safari" + } + ] + }, + { + "@id": "812de653-b4bc-47e2-b445-27ddbfe987c6", + "@type": "uco-observable:ObservableObject", + "uco-core:hasFacet": [ + { + "@id": "bf2f8aca-9951-4f33-8930-aea3a04811de", + "@type": "uco-observable:URLHistoryFacet", + "uco-observable:browserInformation": { + "@id": "2382da9b-1038-4c8c-bdd6-723d8ec0cc78" + }, + "uco-observable:urlHistoryEntry": [ + { + "@id": "d2e288e1-da50-4617-8e60-9724400efe6e", + "@type": "uco-observable:URLHistoryEntry", + "uco-observable:browserUserProfile": "Jill", + "uco-observable:expirationTime": { + "@type": "xsd:dateTime", + "@value": "2024-12-27T14:55:01+00:00" + }, + "uco-observable:firstVisit": { + "@type": "xsd:dateTime", + "@value": "2024-01-02T15:55:01+00:00" + }, + "uco-observable:hostname": "case_test", + "uco-observable:keywordSearchTerm": "docker", + "uco-observable:lastVisit": { + "@type": "xsd:dateTime", + "@value": "2024-02-10T10:55:01+00:00" + }, + "uco-observable:manuallyEnteredCount": { + "@type": "xsd:nonNegativeInteger", + "@value": "10" + }, + "uco-observable:pageTitle": "Docker tutorial", + "uco-observable:url": { + "@id": "2768d96c-cbc0-437f-bae8-6341427410cf" + }, + "uco-observable:visitCount": { + "@type": "xsd:integer", + "@value": "18" + } + }, + { + "@id": "6624f983-30fe-4bdb-9e8a-6567558ef00d", + "@type": "uco-observable:URLHistoryEntry", + "uco-observable:browserUserProfile": "Tamasin", + "uco-observable:expirationTime": { + "@type": "xsd:dateTime", + "@value": "2024-12-27T14:55:01+00:00" + }, + "uco-observable:firstVisit": { + "@type": "xsd:dateTime", + "@value": "2024-01-02T15:55:01+00:00" + }, + "uco-observable:hostname": "case_test", + "uco-observable:keywordSearchTerm": "git actions", + "uco-observable:lastVisit": { + "@type": "xsd:dateTime", + "@value": "2024-02-10T10:55:01+00:00" + }, + "uco-observable:manuallyEnteredCount": { + "@type": "xsd:nonNegativeInteger", + "@value": "21" + }, + "uco-observable:pageTitle": "GitHub actions tutorial", + "uco-observable:url": { + "@id": "2768d96c-cbc0-437f-bae8-6341427410cf" + }, + "uco-observable:visitCount": { + "@type": "xsd:integer", + "@value": "38" + } + } + ] + } + ] + }, + { + "@id": "8c1630af-897f-4db9-876c-0026b0706904", + "@type": "uco-observable:ObservableObject", + "uco-core:hasFacet": [ + { + "@id": "dcaa9c52-c779-4f12-ba3d-314c96d9ee3b", "@type": "uco-observable:PhoneAccountFacet", "uco-observable:phoneNumber": "123456" } ] }, { - "@id": "9af4324d-cb7b-54d7-aba4-95714396435e", + "@id": "46b0bc38-2921-4085-bf88-92a1a37ad155", "@type": "uco-observable:ObservableObject", "uco-core:hasFacet": [ { - "@id": "d5797860-8d73-5a53-a6fc-660a4e8c64e2", + "@id": "04a6af51-43b0-472f-86cb-43cdad004a2e", "@type": "uco-observable:PhoneAccountFacet", "uco-observable:phoneNumber": "987654" } ] }, { - "@id": "997f05df-f309-588d-829f-c20e3cbadb57", + "@id": "14866732-0a29-4af5-b702-900124feef94", "@type": "uco-observable:ObservableObject", "uco-core:hasFacet": [ { - "@id": "5860f49a-8471-5984-9610-402672d63f56", + "@id": "6269aabf-eb17-4e9c-8583-0cda2357864d", "@type": "uco-observable:MessageFacet", "uco-observable:messageText": "Are you free this weekend?", "uco-observable:sentTime": { @@ -324,39 +423,39 @@ "@value": "2023-01-01T01:08:08.000008+00:00" }, "uco-observable:from": { - "@id": "41bc2540-fdd8-5753-864c-00a5de7d0d2e" + "@id": "8c1630af-897f-4db9-876c-0026b0706904" }, "uco-observable:to": [ { - "@id": "41bc2540-fdd8-5753-864c-00a5de7d0d2e" + "@id": "8c1630af-897f-4db9-876c-0026b0706904" }, { - "@id": "9af4324d-cb7b-54d7-aba4-95714396435e" + "@id": "46b0bc38-2921-4085-bf88-92a1a37ad155" } ], "uco-observable:application": { - "@id": "697d25c6-00bd-571e-aebd-5a96e34ea4b5" + "@id": "aeb36b76-ac6b-4c6f-b2da-2253f36689de" } } ] }, { - "@id": "697d25c6-00bd-571e-aebd-5a96e34ea4b5", + "@id": "aeb36b76-ac6b-4c6f-b2da-2253f36689de", "@type": "uco-observable:ObservableObject", "uco-core:hasFacet": [ { - "@id": "641fc5c4-49a3-573e-bdb2-d6d8dc000fee", + "@id": "79164964-cda0-4a25-a293-05908095e953", "@type": "uco-observable:ApplicationFacet", "uco-core:name": "WhatsApp" } ] }, { - "@id": "6e0ac6ce-6afc-58c6-b4b6-501fa5de2699", + "@id": "00470c95-1808-45f4-bdc3-6b037da0cd3e", "@type": "uco-identity:Identity", "uco-core:hasFacet": [ { - "@id": "3d6f634e-7c5d-5ff8-b996-8d738bb2793a", + "@id": "87f33261-7b83-4e81-998c-56a15fc41e71", "@type": "uco-identity:BirthInformationFacet", "uco-identity:birthdate": { "@type": "xsd:dateTime", @@ -364,7 +463,7 @@ } }, { - "@id": "00f08d34-2abd-53e3-b758-cd5ba00300c9", + "@id": "d3e5065c-1fff-40b8-9d20-604eecd50655", "@type": "uco-identity:SimpleNameFacet", "uco-identity:givenName": "Davey", "uco-identity:familyName": "Jones" @@ -372,11 +471,11 @@ ] }, { - "@id": "17525359-a042-5503-ad5f-f8f9bcd53472", + "@id": "aaa6befd-96b9-4d68-99b6-152ecd6b087b", "@type": "uco-location:Location", "uco-core:hasFacet": [ { - "@id": "1c09087a-0577-5025-a23b-c3e0155a5bf7", + "@id": "9f562f7c-943c-4478-80bf-a97831ff2196", "@type": "uco-location:LatLongCoordinatesFacet", "uco-location:latitude": { "@type": "xsd:decimal", @@ -390,18 +489,18 @@ ] }, { - "@id": "80c8f41b-e720-51db-b650-1df11b5f2acb", + "@id": "8d093b40-4de9-4bd3-b14c-a659b6db89d3", "@type": "case-investigation:Investigation", "uco-core:name": "Crime A", "case-investigation:focus": "Transfer of Illicit Materials", "uco-core:description": "Inquiry into the transfer of illicit materials and the devices used to do so", "uco-core:object": [ { - "@id": "fa07818e-5832-5d5f-81b4-f7e143e98252", + "@id": "d217a13f-4e45-4761-9ce3-75b4bb96245d", "@type": "uco-observable:ObservableObject", "uco-core:hasFacet": [ { - "@id": "9ab260ac-cdf5-5524-9047-3ea85ebfb6e2", + "@id": "ed8e9074-075a-4164-adf7-e6ad07aa3554", "@type": "uco-observable:FileFacet", "uco-observable:fileSystemType": "EXT4", "uco-observable:fileName": "IMG_0123.jpg", @@ -413,7 +512,7 @@ } }, { - "@id": "09cab366-d82b-57f8-a94c-9814bf7ef63d", + "@id": "82b261be-f5eb-4958-b6eb-80032e45577d", "@type": "uco-observable:ContentDataFacet", "uco-observable:magicNumber": "/9j/ww==", "uco-observable:mimeType": "image/jpg", @@ -428,7 +527,7 @@ }, "uco-observable:hash": [ { - "@id": "84c0e1ca-a4da-59f3-b60a-096983862089", + "@id": "a63a7b6d-897c-409d-871a-7cae45888fcd", "@type": "uco-types:Hash", "uco-types:hashMethod": { "@type": "uco-vocabulary:HashNameVocab", @@ -442,7 +541,7 @@ ] }, { - "@id": "f3ce4eee-0a5d-54a7-9cc7-5ac043234d77", + "@id": "82425d96-24df-407d-b850-a59664d52973", "@type": "uco-observable:RasterPictureFacet", "uco-observable:pictureType": "jpg", "uco-observable:pictureHeight": { @@ -459,20 +558,20 @@ } }, { - "@id": "d6585cd5-ad4b-54c3-a649-d45d12d1605d", + "@id": "97755a73-f374-45d9-b8d5-7e5768715afc", "@type": "uco-observable:EXIFFacet", "uco-observable:exifData": { - "@id": "ab4cd39a-dea8-558b-a49e-4aedd9aff7f1", + "@id": "03ac244a-c031-4c01-abea-3efc8e6c5eab", "@type": "uco-types:ControlledDictionary", "uco-types:entry": [ { - "@id": "7721bd93-fff7-5e1d-a14e-2d627c399ebe", + "@id": "946fee94-2e9c-4219-8c29-793ff26a526e", "@type": "uco-types:ControlledDictionaryEntry", "uco-types:key": "Make", "uco-types:value": "Canon" }, { - "@id": "9ed3e5de-cde8-5cec-a71b-774e485da67d", + "@id": "85f1a4b2-b71e-430a-aec9-6540e05a7366", "@type": "uco-types:ControlledDictionaryEntry", "uco-types:key": "Model", "uco-types:value": "Powershot" @@ -483,7 +582,7 @@ ] }, { - "@id": "268ab394-417e-579a-871b-84947adc9926", + "@id": "f995e449-4fb5-484b-a99a-b63343e7702f", "@type": "case-investigation:InvestigativeAction", "uco-core:name": "annotated", "uco-action:startTime": { @@ -496,10 +595,10 @@ }, "uco-core:hasFacet": [ { - "@id": "102c218a-bc39-52e6-8228-abbc200378cf", + "@id": "5016338f-a862-476c-bbb7-18b33367c91f", "@type": "uco-observable:DeviceFacet", "uco-observable:manufacturer": { - "@id": "2ed228fb-6c23-567a-8128-c891ccdb0dc6" + "@id": "8e18c77d-73e3-4569-b1fb-a0af403da57a" }, "uco-observable:deviceType": "iPhone", "uco-observable:model": "6XS", @@ -510,7 +609,7 @@ ] }, { - "@id": "0336f3a3-ff74-5e34-97bb-14534316f393", + "@id": "03c4a175-2baf-4346-bf47-c96f2ab558cd", "@type": "uco-observable:Message", "uco-observable:state": "some state", "uco-observable:hasChanged": { @@ -521,45 +620,45 @@ "olo:slot": null, "uco-core:hasFacet": [ { - "@id": "6d5906a1-df3f-5aa3-8c0d-c76c570d8b96", + "@id": "57996b83-51a7-48cf-a941-a1dd7bde540c", "@type": "uco-observable:MessageFacet" } ] }, { - "@id": "24cd8876-1c97-5fce-9976-8733f6e901f6", + "@id": "49934f4b-6eb8-464c-9306-e039c8850c71", "@type": "uco-observable:Message", "olo:length": null, "olo:slot": null, "uco-core:hasFacet": [ { - "@id": "742d8932-3fe2-5c0f-afc4-9dd2e20533e3", + "@id": "5fe1b398-6cad-471e-9554-b136721fc50b", "@type": "uco-observable:MessageFacet" } ] }, { - "@id": "32599b39-84d2-5471-9452-c494171897fd", + "@id": "8023fd10-14f9-48f9-adeb-5b8cc863c44b", "@type": "uco-observable:Message", "olo:length": null, "olo:slot": null, "uco-core:hasFacet": [ { - "@id": "6f716a17-3a29-513a-9e21-8bf6112c1d50", + "@id": "83abaf69-f26e-4d0d-bd7d-49667e9e2372", "@type": "uco-observable:MessageFacet" } ] }, { - "@id": "35fb4ae9-6e55-59b7-a797-6aff1494a314", + "@id": "72fb1f29-fc05-4156-9716-faf497fb9384", "@type": "uco-observable:MessageThread", "uco-core:hasFacet": [ { - "@id": "3344007e-c9c4-5437-9766-4001fc9f3af5", + "@id": "1253a5d9-7cb6-4114-945e-e9d6bbde4b85", "@type": "uco-observable:MessageThreadFacet", "uco-observable:displayName": "some name", "uco-observable:messageThread": { - "@id": "562c20c5-bef7-5919-a969-6c820a80e0d3", + "@id": "0189faff-1edd-4d62-a791-8de0531c2255", "@type": "uco-types:Thread", "co:size": { "@type": "xsd:nonNegativeInteger", @@ -567,13 +666,13 @@ }, "co:element": [ { - "@id": "0336f3a3-ff74-5e34-97bb-14534316f393" + "@id": "03c4a175-2baf-4346-bf47-c96f2ab558cd" }, { - "@id": "24cd8876-1c97-5fce-9976-8733f6e901f6" + "@id": "49934f4b-6eb8-464c-9306-e039c8850c71" }, { - "@id": "32599b39-84d2-5471-9452-c494171897fd" + "@id": "8023fd10-14f9-48f9-adeb-5b8cc863c44b" } ] } diff --git a/case_mapping/uco/observable.py b/case_mapping/uco/observable.py index b72b9c7..4a59a25 100644 --- a/case_mapping/uco/observable.py +++ b/case_mapping/uco/observable.py @@ -376,79 +376,164 @@ class FacetUrlHistory(FacetEntity): def __init__(self, browser=None, history_entries=None): """ :param browser_info: An observable object containing a URLHistoryFacet - :param history_entries: A list of URLHistoryEntry types + :param history_entries: A list of dictionaries, each dict has the + following keys: + "uco-observable:browserUserProfile": str, + "uco-observable:expirationTime" : datetime, + "uco-observable:firstVisit": datetime, + "uco-observable:hostname": str, + "uco-observable:keywordSearchTerm": str, + "uco-observable:lastVisit" : datetime, + "uco-observable:manuallyEnteredCount": non negative int + "uco-observable:pageTitle": str, + "uco-observable:ble:referrerUrl": url_object, + "uco-observable:url": url_object, + "uco-observable:visitCount": int, """ super().__init__() self["@type"] = "uco-observable:URLHistoryFacet" self._node_reference_vars( **{ "uco-observable:browserInformation": browser, - "uco-observable:urlHistoryEntry": history_entries, } ) - -class UrlHistoryEntry(FacetEntity): - def __init__( - self, - browser_user_profile=None, - expiration_time=None, - first_visit=None, - host_name=None, - keyword_search_term=None, - last_visit=None, - manually_entered_count=None, - page_title=None, - referrer_url=None, - url=None, - visit_count=None, - ): - """ - :param browser_user_profile: The web browser user profile for which the URL history entry was created. - :param expiration_time: The date and time at which the validity of the object expires. - :param first_visit: The date/time that the URL referred to by the URL field was first visited. - :param host_name: The hostname of the system. - :param keyword_search_term: The string representing a keyword search term contained within the URL field. - :param last_visit: The date/time that the URL referred to by the URL field was last visited. - :param manually_entered_count: The number of times the URL referred to by the URL field was manually entered into the browser's address field by the user. - :param page_title: The title of a web page - :param referrer_url: The origination point (i.e., URL) of a URL request. - :param url: An observable object with a URLFacet. - :param visit_count: The number of times a URL has been visited by a particular web browser. - """ - - super().__init__() - self["@type"] = "uco-observable:URLHistoryEntry" - self._str_vars( - **{ - "uco-observable:browserUserProfile": browser_user_profile, - "uco-observable:hostname": host_name, - "uco-observable:pageTitle": page_title, - "uco-observable:keywordSearchTerm": keyword_search_term, - } + keys_str = ( + "uco-observable:browserUserProfile", + "uco-observable:hostname", + "uco-observable:pageTitle", + "uco-observable:keywordSearchTerm", + "uco-observable:urlHistoryEntry", ) - self._int_vars(**{"uco-observable:visitCount": visit_count}) - self._datetime_vars( - **{ - "uco-observable:firstVisit": first_visit, - "uco-observable:lastVisit": last_visit, - "uco-observable:expirationTime": expiration_time, - } + keys_datetime = ( + "uco-observable:firstVisit", + "uco-observable:lastVisit", + "uco-observable:expirationTime", ) - self._node_reference_vars( - **{ - "uco-observable:ble:referrerUrl": referrer_url, - "uco-observable:url": url, - } - ) - # TODO AJN: This is one instance of xsd:nonNegativeInteger. - # I'm uncertain at the moment if there are other instances in - # the ontology requiring nonNegativeIntegers; if so, the - # FacetEntity class needs to have a helper function added. - self["uco-observable:manuallyEnteredCount"] = { - "@type": "xsd:nonNegativeInteger", - "@value": "%d" % manually_entered_count, - } + keys_int = "uco-observable:visitCount" + keys_ref = ("uco-observable:ble:referrerUrl", "uco-observable:url") + + self["uco-observable:urlHistoryEntry"] = [] + for entry in history_entries: + history_entry = {} + history_entry["@id"] = local_uuid() + history_entry["@type"] = "uco-observable:URLHistoryEntry" + for key, var in entry.items(): + if key in keys_str: + if isinstance(var, str): + history_entry[key] = var + else: + self.__handle_var_type_errors(key, var, "str") + elif key in keys_datetime: + if isinstance(var, datetime): + tz_info = var.strftime("%z") + iso_format = ( + var.isoformat() if tz_info else var.isoformat() + "+00:00" + ) + history_entry[key] = { + "@type": "xsd:dateTime", + "@value": iso_format, + } + else: + self.__handle_var_type_errors(key, var, "datetime") + elif key in keys_int: + if isinstance(var, int): + history_entry[key] = { + "@type": "xsd:integer", + "@value": str(var), + } + else: + self.__handle_var_type_errors(key, var, "int") + elif key in keys_ref: + if isinstance(var, list) or isinstance(var, tuple): + is_object_entity = [ + isinstance(item, ObjectEntity) for item in var + ] + if all(is_object_entity): + history_entry[key] = [ + {"@id": item.get_id()} for item in var + ] + else: + self.__handle_list_type_errors( + key, var, "ObjectEntity (no @id key)" + ) + elif isinstance(var, ObjectEntity): + history_entry[key] = {"@id": var.get_id()} + else: + self.__handle_var_type_errors( + key, var, "ObjectEntity (no @id key)" + ) + elif key == "uco-observable:manuallyEnteredCount": + history_entry[key] = { + "@type": "xsd:nonNegativeInteger", + "@value": "%d" % var, + } + + self["uco-observable:urlHistoryEntry"].append(history_entry) + + +# class UrlHistoryEntry(FacetEntity): +# It's no longer necessary, all data are included in the above FacetUrlHistory class +# def __init__( +# self, +# browser_user_profile=None, +# expiration_time=None, +# first_visit=None, +# host_name=None, +# keyword_search_term=None, +# last_visit=None, +# manually_entered_count=None, +# page_title=None, +# referrer_url=None, +# url=None, +# visit_count=None, +# ): +# """ +# :param browser_user_profile: The web browser user profile for which the URL history entry was created. +# :param expiration_time: The date and time at which the validity of the object expires. +# :param first_visit: The date/time that the URL referred to by the URL field was first visited. +# :param host_name: The hostname of the system. +# :param keyword_search_term: The string representing a keyword search term contained within the URL field. +# :param last_visit: The date/time that the URL referred to by the URL field was last visited. +# :param manually_entered_count: The number of times the URL referred to by the URL field was manually entered into the browser's address field by the user. +# :param page_title: The title of a web page +# :param referrer_url: The origination point (i.e., URL) of a URL request. +# :param url: An observable object with a URLFacet. +# :param visit_count: The number of times a URL has been visited by a particular web browser. +# """ + +# super().__init__() +# self["@type"] = "uco-observable:URLHistoryEntry" +# self._str_vars( +# **{ +# "uco-observable:browserUserProfile": browser_user_profile, +# "uco-observable:hostname": host_name, +# "uco-observable:pageTitle": page_title, +# "uco-observable:keywordSearchTerm": keyword_search_term, +# } +# ) +# self._int_vars(**{"uco-observable:visitCount": visit_count}) +# self._datetime_vars( +# **{ +# "uco-observable:firstVisit": first_visit, +# "uco-observable:lastVisit": last_visit, +# "uco-observable:expirationTime": expiration_time, +# } +# ) +# self._node_reference_vars( +# **{ +# "uco-observable:ble:referrerUrl": referrer_url, +# "uco-observable:url": url, +# } +# ) +# # TODO AJN: This is one instance of xsd:nonNegativeInteger. +# # I'm uncertain at the moment if there are other instances in +# # the ontology requiring nonNegativeIntegers; if so, the +# # FacetEntity class needs to have a helper function added. +# self["uco-observable:manuallyEnteredCount"] = { +# "@type": "xsd:nonNegativeInteger", +# "@value": "%d" % manually_entered_count, +# } class FacetUrl(FacetEntity): @@ -1403,7 +1488,7 @@ def __init__(self, disk_type=None, size=None, partition=None): "uco-observable:BluetoothAddressFacet": BluetoothAddress, "uco-observable:ObservableObject": ObservableObject, "uco-observable:URLHistoryFacet": FacetUrlHistory, - "uco-observable:URLHistoryEntry": UrlHistoryEntry, + # "uco-observable:URLHistoryEntry": UrlHistoryEntry, "uco-observable:URLFacet": FacetUrl, "uco-observable:RasterPictureFacet": FacetRasterPicture, "uco-observable:CallFacet": FacetCall, diff --git a/example.py b/example.py index 149bf66..0f2d43d 100644 --- a/example.py +++ b/example.py @@ -5,7 +5,7 @@ from case_mapping import case, uco # This is part of enabling non-random UUIDs for the demonstration -# output. The other part is handled at call time, and can be seen in +# output. The other part is handled at call time, and can be seen in # the documentation for cdo_local_uuid._demo_uuid(). cdo_local_uuid.configure() @@ -14,7 +14,10 @@ def _next_timestamp() -> datetime: """ - This example previously used datetime.now(timezone.utc) to generate timestamps. This function instead creates a fixed-value timestamp sequence, to reduce diff noise from timestamps when re-running the example script. + This example previously used datetime.now(timezone.utc) to generate + timestamps. This function instead creates a fixed-value timestamp + sequence, to reduce diff noise from timestamps when re-running the + example script. """ global _current_timestamp_count base_timestamp = datetime(2023, 1, 1, 1, 1, 1, 1, timezone.utc) @@ -118,7 +121,7 @@ def _next_timestamp() -> datetime: bundle.append_to_uco_object(cyber_rel1) ############################## -# Adding an Email Account # # NOTE: Changes here compared to previous version +# Adding an Email Account # ############################## email_address_object_1 = uco.observable.ObservableObject() email_address_1 = uco.observable.FacetEmailAddress( @@ -167,38 +170,53 @@ def _next_timestamp() -> datetime: url_object.append_facets(url_facet) bundle.append_to_uco_object(url_object) +browser_object = uco.observable.ObservableObject() +browser_facet = uco.observable.FacetApplication(app_name="Safari") +browser_object.append_facets(browser_facet) +bundle.append_to_uco_object(browser_object) + url_date_expiration = datetime.strptime("2024-12-27T14:55:01", "%Y-%m-%dT%H:%M:%S") url_date_first = datetime.strptime("2024-01-02T15:55:01", "%Y-%m-%dT%H:%M:%S") url_date_last = datetime.strptime("2024-02-10T10:55:01", "%Y-%m-%dT%H:%M:%S") -url_history_entry_object = uco.observable.ObservableObject() -url_history_entry = uco.observable.UrlHistoryEntry( - browser_user_profile="Jill", - expiration_time=url_date_expiration, - first_visit=url_date_first, - host_name="case_test", - keyword_search_term="docker", - last_visit=url_date_last, - manually_entered_count=10, - page_title="Docker tutorial", - referrer_url=url_object, - url=url_object, - visit_count=18, -) -url_history_entry_object.append_facets(url_history_entry) -bundle.append_to_uco_object(url_history_entry_object) +history_entries = [] +history_entry_1 = { + "uco-observable:browserUserProfile": "Jill", + "uco-observable:expirationTime": url_date_expiration, + "uco-observable:firstVisit": url_date_first, + "uco-observable:hostname": "case_test", + "uco-observable:keywordSearchTerm": "docker", + "uco-observable:lastVisit": url_date_last, + "uco-observable:manuallyEnteredCount": 10, + "uco-observable:pageTitle": "Docker tutorial", + "uco-observable:referrerUrl": url_object, + "uco-observable:url": url_object, + "uco-observable:visitCount": 18, +} +history_entry_2 = { + "uco-observable:browserUserProfile": "Tamasin", + "uco-observable:expirationTime": url_date_expiration, + "uco-observable:firstVisit": url_date_first, + "uco-observable:hostname": "case_test", + "uco-observable:keywordSearchTerm": "git actions", + "uco-observable:lastVisit": url_date_last, + "uco-observable:manuallyEnteredCount": 21, + "uco-observable:pageTitle": "GitHub actions tutorial", + "uco-observable:referrerUrl": url_object, + "uco-observable:url": url_object, + "uco-observable:visitCount": 38, +} -browser_object = uco.observable.ObservableObject() -browser_facet = uco.observable.FacetApplication(app_name="Safari") -browser_object.append_facets(browser_facet) -bundle.append_to_uco_object(browser_object) +url_history_entry_object = uco.observable.ObservableObject() -url_history_object = uco.observable.ObservableObject() +history_entries.append(history_entry_1) +history_entries.append(history_entry_2) url_history_facet = uco.observable.FacetUrlHistory( - browser=browser_object, history_entries=[url_history_entry_object] + browser=browser_object, history_entries=history_entries ) -url_history_object.append_facets(url_history_facet) -bundle.append_to_uco_object(url_history_object) + +url_history_entry_object.append_facets(url_history_facet) +bundle.append_to_uco_object(url_history_entry_object) ############################ From fb64867e8a8ade2ecc04d5c989f027c223cf80b2 Mon Sep 17 00:00:00 2001 From: Alex Nelson Date: Tue, 12 Mar 2024 11:15:43 -0400 Subject: [PATCH 17/22] Apply Python and JSON-LD type-review for coordinates JSON-LD needs the value with the `@value` key to be a JSON string. Some undesired type-coercion could happen if ints or floats are used. Python type signatures are added to check data flow consistency. Signed-off-by: Alex Nelson --- mix_utils/test_duplicate.py | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/mix_utils/test_duplicate.py b/mix_utils/test_duplicate.py index dc35f31..1c876e3 100644 --- a/mix_utils/test_duplicate.py +++ b/mix_utils/test_duplicate.py @@ -1,4 +1,5 @@ import uuid +from typing import Any, Union import utils @@ -39,12 +40,14 @@ def generateTraceAppName(app_name, uuid): return observable -def generateTraceLocationCoordinate(latitude, longitude, uuid): +def generateTraceLocationCoordinate( + latitude: Union[float, str], longitude: Union[float, str], uuid: str +) -> dict[str, Any]: observable = { "@type": "uco-location:LatLongCoordinatesFacet", "@id": uuid, - "uco-location:latitude": {"@type": "xsd:decimal", "@value": latitude}, - "uco-location:longitude": {"@type": "xsd:decimal", "@value": longitude}, + "uco-location:latitude": {"@type": "xsd:decimal", "@value": str(latitude)}, + "uco-location:longitude": {"@type": "xsd:decimal", "@value": str(longitude)}, } return observable @@ -104,8 +107,8 @@ def test_geo_coordinates(): assert geo_object == { "@type": "uco-location:LatLongCoordinatesFacet", "@id": uuid_1, - "uco-location:latitude": {"@type": "xsd:decimal", "@value": lat_1}, - "uco-location:longitude": {"@type": "xsd:decimal", "@value": long_1}, + "uco-location:latitude": {"@type": "xsd:decimal", "@value": str(lat_1)}, + "uco-location:longitude": {"@type": "xsd:decimal", "@value": str(long_1)}, } # print(f"\n 3) FT geo_coordinates={geo_coordinates}") assert geo_coordinates == [ @@ -122,13 +125,13 @@ def test_geo_coordinates(): { "@type": "uco-location:LatLongCoordinatesFacet", "@id": uuid_1, - "uco-location:latitude": {"@type": "xsd:decimal", "@value": lat_1}, - "uco-location:longitude": {"@type": "xsd:decimal", "@value": long_1}, + "uco-location:latitude": {"@type": "xsd:decimal", "@value": str(lat_1)}, + "uco-location:longitude": {"@type": "xsd:decimal", "@value": str(long_1)}, }, { "@type": "uco-location:LatLongCoordinatesFacet", "@id": uuid_2, - "uco-location:latitude": {"@type": "xsd:decimal", "@value": lat_2}, - "uco-location:longitude": {"@type": "xsd:decimal", "@value": long_2}, + "uco-location:latitude": {"@type": "xsd:decimal", "@value": str(lat_2)}, + "uco-location:longitude": {"@type": "xsd:decimal", "@value": str(long_2)}, }, ] From 947307db75bb74a5da753517b89da6bcf57833ca Mon Sep 17 00:00:00 2001 From: Alex Nelson Date: Tue, 12 Mar 2024 11:26:33 -0400 Subject: [PATCH 18/22] Change argument-list type If `Tuple[str, ...]` is used, then each individual positional argument must be a tuple of strings. See PEP-484. `Any` is used instead, to allow for `float`s in the accompanying unit test, and `dict`s in future applications. With these changes, `/mix_utils` passes `mypy`. References: * https://peps.python.org/pep-0484/#arbitrary-argument-lists-and-default-argument-values Disclaimer: Participation by NIST in the creation of the documentation of mentioned software is not intended to imply a recommendation or endorsement by the National Institute of Standards and Technology, nor is it intended to imply that any specific software is necessarily the best available for the purpose. Signed-off-by: Alex Nelson --- mix_utils/utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mix_utils/utils.py b/mix_utils/utils.py index 455efd8..569861d 100644 --- a/mix_utils/utils.py +++ b/mix_utils/utils.py @@ -1,8 +1,8 @@ -from typing import Callable +from typing import Any, Callable def check_value( - *args: tuple[str, ...], + *args: Any, value: str, list_values: list[str], list_objects: list[dict], From 3123a35b2251202264b5c090f7e1a62b6a31c0fc Mon Sep 17 00:00:00 2001 From: Alex Nelson Date: Tue, 12 Mar 2024 11:29:10 -0400 Subject: [PATCH 19/22] Type-annotate mix_utils directory With these changes, `/mix_utils` passes `mypy --strict`. Disclaimer: Participation by NIST in the creation of the documentation of mentioned software is not intended to imply a recommendation or endorsement by the National Institute of Standards and Technology, nor is it intended to imply that any specific software is necessarily the best available for the purpose. Signed-off-by: Alex Nelson --- mix_utils/test_duplicate.py | 26 +++++++++++++++++--------- mix_utils/utils.py | 6 +++--- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/mix_utils/test_duplicate.py b/mix_utils/test_duplicate.py index 1c876e3..32bd7be 100644 --- a/mix_utils/test_duplicate.py +++ b/mix_utils/test_duplicate.py @@ -4,7 +4,9 @@ import utils -def check_app_name(app_name, app_names, app_objects, uuid): +def check_app_name( + app_name: str, app_names: list[str], app_objects: list[dict[str, Any]], uuid: str +) -> dict[str, Any]: # c_check = utils.CheckDuplicate() observable_app = utils.check_value( app_name, @@ -17,7 +19,13 @@ def check_app_name(app_name, app_names, app_objects, uuid): return observable_app -def check_geo_coordinates(latitude, longitude, geo_coordinates, geo_objects, uuid): +def check_geo_coordinates( + latitude: float, + longitude: float, + geo_coordinates: list[str], + geo_objects: list[dict[str, Any]], + uuid: str, +) -> dict[str, Any]: # c_check = utils.CheckDuplicate() observable_app = utils.check_value( latitude, @@ -31,7 +39,7 @@ def check_geo_coordinates(latitude, longitude, geo_coordinates, geo_objects, uui return observable_app -def generateTraceAppName(app_name, uuid): +def generateTraceAppName(app_name: str, uuid: str) -> dict[str, Any]: observable = { "@type": "uco-observable:ApplicationFacet", "@id": uuid, @@ -52,9 +60,9 @@ def generateTraceLocationCoordinate( return observable -def test_app_name(): - app_names = list() - app_objects = list() +def test_app_name() -> None: + app_names: list[str] = list() + app_objects: list[dict[str, Any]] = list() app_1 = "Safari" uuid_1 = "kb:" + str(uuid.uuid4()) check_app_name(app_1, app_names, app_objects, uuid_1) @@ -85,9 +93,9 @@ def test_app_name(): ] -def test_geo_coordinates(): - geo_coordinates = list() - geo_objects = list() +def test_geo_coordinates() -> None: + geo_coordinates: list[str] = list() + geo_objects: list[dict[str, Any]] = list() (lat_1, long_1, uuid_1) = (56.47267913, -71.17069244, "kb:" + str(uuid.uuid4())) check_geo_coordinates(lat_1, long_1, geo_coordinates, geo_objects, uuid_1) # print(f"\n 1) FT geo_coordinates={geo_coordinates}") diff --git a/mix_utils/utils.py b/mix_utils/utils.py index 569861d..ae3d9b8 100644 --- a/mix_utils/utils.py +++ b/mix_utils/utils.py @@ -5,9 +5,9 @@ def check_value( *args: Any, value: str, list_values: list[str], - list_objects: list[dict], - observable_generating_f: Callable[..., dict] -) -> dict: + list_objects: list[dict[str, Any]], + observable_generating_f: Callable[..., dict[str, Any]] +) -> dict[str, Any]: """It checks if a specific value has been already generated related to an ObservableObject relying on the list of its values. This is meant to avoid duplication in the JSON/CASE file generated by the parsers (UFED, AXIOM etc.). From e72002e2c92a8c83565c84a23a14ba34d4dde9ee Mon Sep 17 00:00:00 2001 From: Alex Nelson Date: Tue, 12 Mar 2024 11:35:06 -0400 Subject: [PATCH 20/22] Link Issue Signed-off-by: Alex Nelson --- case_mapping/uco/observable.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/case_mapping/uco/observable.py b/case_mapping/uco/observable.py index 4a59a25..2fb00ba 100644 --- a/case_mapping/uco/observable.py +++ b/case_mapping/uco/observable.py @@ -527,9 +527,10 @@ def __init__(self, browser=None, history_entries=None): # } # ) # # TODO AJN: This is one instance of xsd:nonNegativeInteger. -# # I'm uncertain at the moment if there are other instances in -# # the ontology requiring nonNegativeIntegers; if so, the -# # FacetEntity class needs to have a helper function added. +# # There are other instances in the ontology requiring +# # nonNegativeIntegers. Hence, the FacetEntity class needs to +# # have a helper function added. +# # https://github.com/casework/CASE-Mapping-Python/issues/37 # self["uco-observable:manuallyEnteredCount"] = { # "@type": "xsd:nonNegativeInteger", # "@value": "%d" % manually_entered_count, From 6aaa572f17b6b3b6f725d4705e0e33d33392d239 Mon Sep 17 00:00:00 2001 From: Alex Nelson Date: Tue, 12 Mar 2024 11:38:36 -0400 Subject: [PATCH 21/22] Regenerate Make-managed file Signed-off-by: Alex Nelson --- case.jsonld | 188 ++++++++++++++++++++++++++-------------------------- 1 file changed, 94 insertions(+), 94 deletions(-) diff --git a/case.jsonld b/case.jsonld index a126f70..f87e17a 100644 --- a/case.jsonld +++ b/case.jsonld @@ -1,5 +1,5 @@ { - "@id": "0caf82e9-c20e-447d-9b3d-8db30216847f", + "@id": "aef8e4c4-db83-59fd-8f71-b65cd7676c0a", "@context": { "@vocab": "http://caseontology.org/core#", "case-investigation": "https://ontology.caseontology.org/case/investigation/", @@ -21,30 +21,30 @@ "uco-core:description": "An Example Case File", "uco-core:object": [ { - "@id": "b41b368f-1ca3-49c1-94f1-7cfb30a6322c", + "@id": "307df50e-0116-5c51-904c-d5aa69279018", "@type": "uco-identity:Organization", "uco-core:name": "Nikon" }, { - "@id": "9f43661d-d362-4867-b6a4-91940fffacbf", + "@id": "1ccd06e1-1a39-53c2-8e70-86ad7c48ec5c", "@type": "uco-observable:ObservableObject", "uco-core:hasFacet": [ { - "@id": "0073f0da-01e3-40a4-aeba-b007f53f7e2b", + "@id": "e26d7f7d-081c-58e5-8990-77ec13d60d89", "@type": "uco-observable:DeviceFacet", "uco-observable:manufacturer": { - "@id": "b41b368f-1ca3-49c1-94f1-7cfb30a6322c" + "@id": "307df50e-0116-5c51-904c-d5aa69279018" }, "uco-observable:model": "D750" } ] }, { - "@id": "d217a13f-4e45-4761-9ce3-75b4bb96245d", + "@id": "fa07818e-5832-5d5f-81b4-f7e143e98252", "@type": "uco-observable:ObservableObject", "uco-core:hasFacet": [ { - "@id": "ed8e9074-075a-4164-adf7-e6ad07aa3554", + "@id": "9ab260ac-cdf5-5524-9047-3ea85ebfb6e2", "@type": "uco-observable:FileFacet", "uco-observable:fileSystemType": "EXT4", "uco-observable:fileName": "IMG_0123.jpg", @@ -56,7 +56,7 @@ } }, { - "@id": "82b261be-f5eb-4958-b6eb-80032e45577d", + "@id": "09cab366-d82b-57f8-a94c-9814bf7ef63d", "@type": "uco-observable:ContentDataFacet", "uco-observable:magicNumber": "/9j/ww==", "uco-observable:mimeType": "image/jpg", @@ -71,7 +71,7 @@ }, "uco-observable:hash": [ { - "@id": "a63a7b6d-897c-409d-871a-7cae45888fcd", + "@id": "84c0e1ca-a4da-59f3-b60a-096983862089", "@type": "uco-types:Hash", "uco-types:hashMethod": { "@type": "uco-vocabulary:HashNameVocab", @@ -85,7 +85,7 @@ ] }, { - "@id": "82425d96-24df-407d-b850-a59664d52973", + "@id": "f3ce4eee-0a5d-54a7-9cc7-5ac043234d77", "@type": "uco-observable:RasterPictureFacet", "uco-observable:pictureType": "jpg", "uco-observable:pictureHeight": { @@ -102,20 +102,20 @@ } }, { - "@id": "97755a73-f374-45d9-b8d5-7e5768715afc", + "@id": "d6585cd5-ad4b-54c3-a649-d45d12d1605d", "@type": "uco-observable:EXIFFacet", "uco-observable:exifData": { - "@id": "03ac244a-c031-4c01-abea-3efc8e6c5eab", + "@id": "ab4cd39a-dea8-558b-a49e-4aedd9aff7f1", "@type": "uco-types:ControlledDictionary", "uco-types:entry": [ { - "@id": "946fee94-2e9c-4219-8c29-793ff26a526e", + "@id": "7721bd93-fff7-5e1d-a14e-2d627c399ebe", "@type": "uco-types:ControlledDictionaryEntry", "uco-types:key": "Make", "uco-types:value": "Canon" }, { - "@id": "85f1a4b2-b71e-430a-aec9-6540e05a7366", + "@id": "9ed3e5de-cde8-5cec-a71b-774e485da67d", "@type": "uco-types:ControlledDictionaryEntry", "uco-types:key": "Model", "uco-types:value": "Powershot" @@ -126,12 +126,12 @@ ] }, { - "@id": "8e18c77d-73e3-4569-b1fb-a0af403da57a", + "@id": "2ed228fb-6c23-567a-8128-c891ccdb0dc6", "@type": "uco-identity:Organization", "uco-core:name": "Apple" }, { - "@id": "f995e449-4fb5-484b-a99a-b63343e7702f", + "@id": "268ab394-417e-579a-871b-84947adc9926", "@type": "case-investigation:InvestigativeAction", "uco-core:name": "annotated", "uco-action:startTime": { @@ -144,10 +144,10 @@ }, "uco-core:hasFacet": [ { - "@id": "5016338f-a862-476c-bbb7-18b33367c91f", + "@id": "102c218a-bc39-52e6-8228-abbc200378cf", "@type": "uco-observable:DeviceFacet", "uco-observable:manufacturer": { - "@id": "8e18c77d-73e3-4569-b1fb-a0af403da57a" + "@id": "2ed228fb-6c23-567a-8128-c891ccdb0dc6" }, "uco-observable:deviceType": "iPhone", "uco-observable:model": "6XS", @@ -156,12 +156,12 @@ ] }, { - "@id": "09d797d5-15fe-4549-9e3e-5cf6c5e2cb33", + "@id": "f3f8c0fb-6a78-5547-aa4f-7c995d9a8732", "@type": "uco-identity:Organization", "uco-core:name": "oneplus" }, { - "@id": "8baf2e57-1378-4693-9e0c-15ef0175eacb", + "@id": "19ff5a9d-1d15-5a6f-858d-28528d5f90d9", "@type": "case-investigation:InvestigativeAction", "uco-core:name": "annotated", "uco-action:startTime": { @@ -174,10 +174,10 @@ }, "uco-core:hasFacet": [ { - "@id": "6f1ae468-7a6d-46cd-b6c0-22f8081bf578", + "@id": "713d0464-a90c-52f3-88f4-1f0a4a9de091", "@type": "uco-observable:DeviceFacet", "uco-observable:manufacturer": { - "@id": "09d797d5-15fe-4549-9e3e-5cf6c5e2cb33" + "@id": "f3f8c0fb-6a78-5547-aa4f-7c995d9a8732" }, "uco-observable:deviceType": "Android", "uco-observable:model": "8", @@ -186,7 +186,7 @@ ] }, { - "@id": "641a0e7a-b745-416a-9b6f-ca44b71973eb", + "@id": "56004659-8649-5ef2-b2e9-e1dae56877f0", "@type": "uco-observable:ObservableRelationship", "uco-core:isDirectional": { "@type": "xsd:boolean", @@ -194,38 +194,38 @@ }, "uco-core:kindOfRelationship": "Contained_Within", "uco-core:source": { - "@id": "9f43661d-d362-4867-b6a4-91940fffacbf" + "@id": "1ccd06e1-1a39-53c2-8e70-86ad7c48ec5c" }, "uco-core:target": { - "@id": "d217a13f-4e45-4761-9ce3-75b4bb96245d" + "@id": "fa07818e-5832-5d5f-81b4-f7e143e98252" }, "uco-core:hasFacet": [ { - "@id": "a2938dcc-188c-4843-ba6a-a8bbcbc4ebe5", + "@id": "862e79c2-ecab-5798-8c43-09ac916ab0d4", "@type": "uco-observable:PathRelationFacet", "uco-observable:path": "/sdcard/IMG_0123.jpg" } ] }, { - "@id": "fa946986-7505-4ed3-8ec9-d90dab185779", + "@id": "72930cf3-7693-5c80-ae79-ca82151b5d30", "@type": "uco-observable:ObservableObject", "uco-core:hasFacet": [ { - "@id": "22147750-7302-4358-8a7b-c83dd73d854c", + "@id": "5a33cc98-bf34-5011-82ee-1e664da00d29", "@type": "uco-observable:EmailAccountFacet", "uco-observable:emailAddress": { - "@id": "086c1ade-7aea-476d-81d3-d34dd24ddb9b" + "@id": "94adcf50-cad5-5a2d-9be3-e1e42d698028" } } ] }, { - "@id": "086c1ade-7aea-476d-81d3-d34dd24ddb9b", + "@id": "94adcf50-cad5-5a2d-9be3-e1e42d698028", "@type": "uco-observable:ObservableObject", "uco-core:hasFacet": [ { - "@id": "3fc634a8-10cc-45b1-bf23-bdfcf78773a6", + "@id": "069043f2-89ee-532e-892b-d90a55ed987c", "@type": "uco-observable:EmailAddressFacet", "uco-observable:addressValue": "info@example.com", "uco-observable:displayName": "Example User" @@ -233,24 +233,24 @@ ] }, { - "@id": "e50a346b-d97a-4e83-8249-424a643126d6", + "@id": "742fedd8-ec56-5efd-bbae-eac6de464215", "@type": "uco-observable:ObservableObject", "uco-core:hasFacet": [ { - "@id": "0aa38f1b-2500-46f2-8f7a-b60054c18ded", + "@id": "1e2a360e-9b34-5fa9-978d-e28c455e59d0", "@type": "uco-observable:EmailAccountFacet", "uco-observable:emailAddress": { - "@id": "8961e3cc-477e-4d2e-bee3-365289117397" + "@id": "a3f00ca2-db54-51a9-a281-1125bbd37783" } } ] }, { - "@id": "8961e3cc-477e-4d2e-bee3-365289117397", + "@id": "a3f00ca2-db54-51a9-a281-1125bbd37783", "@type": "uco-observable:ObservableObject", "uco-core:hasFacet": [ { - "@id": "c82fc976-be37-4031-8b3e-c22692a4f929", + "@id": "f72553c7-7fa7-5354-aa68-7837e9e7b8c4", "@type": "uco-observable:EmailAddressFacet", "uco-observable:addressValue": "admin@example.com", "uco-observable:displayName": "Example Admin" @@ -258,11 +258,11 @@ ] }, { - "@id": "fbe0f0d4-9b00-46bb-a1f5-fa4a5fb87630", + "@id": "70e47458-fd7d-594d-816e-272d5bb1e440", "@type": "uco-observable:ObservableObject", "uco-core:hasFacet": [ { - "@id": "8c2aa1a8-dd71-4cc7-9a02-0a41caeffb52", + "@id": "60d29bf6-1cb0-5974-866a-2f1d60ee4a51", "@type": "uco-observable:EmailMessageFacet", "uco-observable:subject": "Thoughts on Our Next Book Club Pick?", "uco-observable:body": "Hello fellow bookworms! It's that time again.", @@ -276,54 +276,54 @@ "@value": "2023-01-01T01:06:06.000006+00:00" }, "uco-observable:from": { - "@id": "086c1ade-7aea-476d-81d3-d34dd24ddb9b" + "@id": "94adcf50-cad5-5a2d-9be3-e1e42d698028" }, "uco-observable:to": [ { - "@id": "086c1ade-7aea-476d-81d3-d34dd24ddb9b" + "@id": "94adcf50-cad5-5a2d-9be3-e1e42d698028" }, { - "@id": "8961e3cc-477e-4d2e-bee3-365289117397" + "@id": "a3f00ca2-db54-51a9-a281-1125bbd37783" } ] } ] }, { - "@id": "2768d96c-cbc0-437f-bae8-6341427410cf", + "@id": "41bc2540-fdd8-5753-864c-00a5de7d0d2e", "@type": "uco-observable:ObservableObject", "uco-core:hasFacet": [ { - "@id": "62492c88-8511-4567-a2ff-7df81ae89b83", + "@id": "0f0b71e7-39a8-5249-83a5-b52888d5c8b7", "@type": "uco-observable:URLFacet", "uco-observable:fullValue": "www.docker.com/howto" } ] }, { - "@id": "2382da9b-1038-4c8c-bdd6-723d8ec0cc78", + "@id": "9af4324d-cb7b-54d7-aba4-95714396435e", "@type": "uco-observable:ObservableObject", "uco-core:hasFacet": [ { - "@id": "2f7fb681-6af0-45d0-a618-4bac4407cdc8", + "@id": "d5797860-8d73-5a53-a6fc-660a4e8c64e2", "@type": "uco-observable:ApplicationFacet", "uco-core:name": "Safari" } ] }, { - "@id": "812de653-b4bc-47e2-b445-27ddbfe987c6", + "@id": "997f05df-f309-588d-829f-c20e3cbadb57", "@type": "uco-observable:ObservableObject", "uco-core:hasFacet": [ { - "@id": "bf2f8aca-9951-4f33-8930-aea3a04811de", + "@id": "697d25c6-00bd-571e-aebd-5a96e34ea4b5", "@type": "uco-observable:URLHistoryFacet", "uco-observable:browserInformation": { - "@id": "2382da9b-1038-4c8c-bdd6-723d8ec0cc78" + "@id": "9af4324d-cb7b-54d7-aba4-95714396435e" }, "uco-observable:urlHistoryEntry": [ { - "@id": "d2e288e1-da50-4617-8e60-9724400efe6e", + "@id": "641fc5c4-49a3-573e-bdb2-d6d8dc000fee", "@type": "uco-observable:URLHistoryEntry", "uco-observable:browserUserProfile": "Jill", "uco-observable:expirationTime": { @@ -346,7 +346,7 @@ }, "uco-observable:pageTitle": "Docker tutorial", "uco-observable:url": { - "@id": "2768d96c-cbc0-437f-bae8-6341427410cf" + "@id": "41bc2540-fdd8-5753-864c-00a5de7d0d2e" }, "uco-observable:visitCount": { "@type": "xsd:integer", @@ -354,7 +354,7 @@ } }, { - "@id": "6624f983-30fe-4bdb-9e8a-6567558ef00d", + "@id": "5860f49a-8471-5984-9610-402672d63f56", "@type": "uco-observable:URLHistoryEntry", "uco-observable:browserUserProfile": "Tamasin", "uco-observable:expirationTime": { @@ -377,7 +377,7 @@ }, "uco-observable:pageTitle": "GitHub actions tutorial", "uco-observable:url": { - "@id": "2768d96c-cbc0-437f-bae8-6341427410cf" + "@id": "41bc2540-fdd8-5753-864c-00a5de7d0d2e" }, "uco-observable:visitCount": { "@type": "xsd:integer", @@ -389,33 +389,33 @@ ] }, { - "@id": "8c1630af-897f-4db9-876c-0026b0706904", + "@id": "6e0ac6ce-6afc-58c6-b4b6-501fa5de2699", "@type": "uco-observable:ObservableObject", "uco-core:hasFacet": [ { - "@id": "dcaa9c52-c779-4f12-ba3d-314c96d9ee3b", + "@id": "00f08d34-2abd-53e3-b758-cd5ba00300c9", "@type": "uco-observable:PhoneAccountFacet", "uco-observable:phoneNumber": "123456" } ] }, { - "@id": "46b0bc38-2921-4085-bf88-92a1a37ad155", + "@id": "3d6f634e-7c5d-5ff8-b996-8d738bb2793a", "@type": "uco-observable:ObservableObject", "uco-core:hasFacet": [ { - "@id": "04a6af51-43b0-472f-86cb-43cdad004a2e", + "@id": "17525359-a042-5503-ad5f-f8f9bcd53472", "@type": "uco-observable:PhoneAccountFacet", "uco-observable:phoneNumber": "987654" } ] }, { - "@id": "14866732-0a29-4af5-b702-900124feef94", + "@id": "1c09087a-0577-5025-a23b-c3e0155a5bf7", "@type": "uco-observable:ObservableObject", "uco-core:hasFacet": [ { - "@id": "6269aabf-eb17-4e9c-8583-0cda2357864d", + "@id": "0336f3a3-ff74-5e34-97bb-14534316f393", "@type": "uco-observable:MessageFacet", "uco-observable:messageText": "Are you free this weekend?", "uco-observable:sentTime": { @@ -423,39 +423,39 @@ "@value": "2023-01-01T01:08:08.000008+00:00" }, "uco-observable:from": { - "@id": "8c1630af-897f-4db9-876c-0026b0706904" + "@id": "6e0ac6ce-6afc-58c6-b4b6-501fa5de2699" }, "uco-observable:to": [ { - "@id": "8c1630af-897f-4db9-876c-0026b0706904" + "@id": "6e0ac6ce-6afc-58c6-b4b6-501fa5de2699" }, { - "@id": "46b0bc38-2921-4085-bf88-92a1a37ad155" + "@id": "3d6f634e-7c5d-5ff8-b996-8d738bb2793a" } ], "uco-observable:application": { - "@id": "aeb36b76-ac6b-4c6f-b2da-2253f36689de" + "@id": "80c8f41b-e720-51db-b650-1df11b5f2acb" } } ] }, { - "@id": "aeb36b76-ac6b-4c6f-b2da-2253f36689de", + "@id": "80c8f41b-e720-51db-b650-1df11b5f2acb", "@type": "uco-observable:ObservableObject", "uco-core:hasFacet": [ { - "@id": "79164964-cda0-4a25-a293-05908095e953", + "@id": "35fb4ae9-6e55-59b7-a797-6aff1494a314", "@type": "uco-observable:ApplicationFacet", "uco-core:name": "WhatsApp" } ] }, { - "@id": "00470c95-1808-45f4-bdc3-6b037da0cd3e", + "@id": "6d5906a1-df3f-5aa3-8c0d-c76c570d8b96", "@type": "uco-identity:Identity", "uco-core:hasFacet": [ { - "@id": "87f33261-7b83-4e81-998c-56a15fc41e71", + "@id": "742d8932-3fe2-5c0f-afc4-9dd2e20533e3", "@type": "uco-identity:BirthInformationFacet", "uco-identity:birthdate": { "@type": "xsd:dateTime", @@ -463,7 +463,7 @@ } }, { - "@id": "d3e5065c-1fff-40b8-9d20-604eecd50655", + "@id": "24cd8876-1c97-5fce-9976-8733f6e901f6", "@type": "uco-identity:SimpleNameFacet", "uco-identity:givenName": "Davey", "uco-identity:familyName": "Jones" @@ -471,11 +471,11 @@ ] }, { - "@id": "aaa6befd-96b9-4d68-99b6-152ecd6b087b", + "@id": "32599b39-84d2-5471-9452-c494171897fd", "@type": "uco-location:Location", "uco-core:hasFacet": [ { - "@id": "9f562f7c-943c-4478-80bf-a97831ff2196", + "@id": "6f716a17-3a29-513a-9e21-8bf6112c1d50", "@type": "uco-location:LatLongCoordinatesFacet", "uco-location:latitude": { "@type": "xsd:decimal", @@ -489,18 +489,18 @@ ] }, { - "@id": "8d093b40-4de9-4bd3-b14c-a659b6db89d3", + "@id": "3344007e-c9c4-5437-9766-4001fc9f3af5", "@type": "case-investigation:Investigation", "uco-core:name": "Crime A", "case-investigation:focus": "Transfer of Illicit Materials", "uco-core:description": "Inquiry into the transfer of illicit materials and the devices used to do so", "uco-core:object": [ { - "@id": "d217a13f-4e45-4761-9ce3-75b4bb96245d", + "@id": "fa07818e-5832-5d5f-81b4-f7e143e98252", "@type": "uco-observable:ObservableObject", "uco-core:hasFacet": [ { - "@id": "ed8e9074-075a-4164-adf7-e6ad07aa3554", + "@id": "9ab260ac-cdf5-5524-9047-3ea85ebfb6e2", "@type": "uco-observable:FileFacet", "uco-observable:fileSystemType": "EXT4", "uco-observable:fileName": "IMG_0123.jpg", @@ -512,7 +512,7 @@ } }, { - "@id": "82b261be-f5eb-4958-b6eb-80032e45577d", + "@id": "09cab366-d82b-57f8-a94c-9814bf7ef63d", "@type": "uco-observable:ContentDataFacet", "uco-observable:magicNumber": "/9j/ww==", "uco-observable:mimeType": "image/jpg", @@ -527,7 +527,7 @@ }, "uco-observable:hash": [ { - "@id": "a63a7b6d-897c-409d-871a-7cae45888fcd", + "@id": "84c0e1ca-a4da-59f3-b60a-096983862089", "@type": "uco-types:Hash", "uco-types:hashMethod": { "@type": "uco-vocabulary:HashNameVocab", @@ -541,7 +541,7 @@ ] }, { - "@id": "82425d96-24df-407d-b850-a59664d52973", + "@id": "f3ce4eee-0a5d-54a7-9cc7-5ac043234d77", "@type": "uco-observable:RasterPictureFacet", "uco-observable:pictureType": "jpg", "uco-observable:pictureHeight": { @@ -558,20 +558,20 @@ } }, { - "@id": "97755a73-f374-45d9-b8d5-7e5768715afc", + "@id": "d6585cd5-ad4b-54c3-a649-d45d12d1605d", "@type": "uco-observable:EXIFFacet", "uco-observable:exifData": { - "@id": "03ac244a-c031-4c01-abea-3efc8e6c5eab", + "@id": "ab4cd39a-dea8-558b-a49e-4aedd9aff7f1", "@type": "uco-types:ControlledDictionary", "uco-types:entry": [ { - "@id": "946fee94-2e9c-4219-8c29-793ff26a526e", + "@id": "7721bd93-fff7-5e1d-a14e-2d627c399ebe", "@type": "uco-types:ControlledDictionaryEntry", "uco-types:key": "Make", "uco-types:value": "Canon" }, { - "@id": "85f1a4b2-b71e-430a-aec9-6540e05a7366", + "@id": "9ed3e5de-cde8-5cec-a71b-774e485da67d", "@type": "uco-types:ControlledDictionaryEntry", "uco-types:key": "Model", "uco-types:value": "Powershot" @@ -582,7 +582,7 @@ ] }, { - "@id": "f995e449-4fb5-484b-a99a-b63343e7702f", + "@id": "268ab394-417e-579a-871b-84947adc9926", "@type": "case-investigation:InvestigativeAction", "uco-core:name": "annotated", "uco-action:startTime": { @@ -595,10 +595,10 @@ }, "uco-core:hasFacet": [ { - "@id": "5016338f-a862-476c-bbb7-18b33367c91f", + "@id": "102c218a-bc39-52e6-8228-abbc200378cf", "@type": "uco-observable:DeviceFacet", "uco-observable:manufacturer": { - "@id": "8e18c77d-73e3-4569-b1fb-a0af403da57a" + "@id": "2ed228fb-6c23-567a-8128-c891ccdb0dc6" }, "uco-observable:deviceType": "iPhone", "uco-observable:model": "6XS", @@ -609,7 +609,7 @@ ] }, { - "@id": "03c4a175-2baf-4346-bf47-c96f2ab558cd", + "@id": "bb9cd337-eb51-50dd-9cee-00f9e9acbf94", "@type": "uco-observable:Message", "uco-observable:state": "some state", "uco-observable:hasChanged": { @@ -620,45 +620,45 @@ "olo:slot": null, "uco-core:hasFacet": [ { - "@id": "57996b83-51a7-48cf-a941-a1dd7bde540c", + "@id": "73af707b-f00e-5327-8fd4-83467b4441c4", "@type": "uco-observable:MessageFacet" } ] }, { - "@id": "49934f4b-6eb8-464c-9306-e039c8850c71", + "@id": "7c4d5cff-deb0-5031-b881-c572d6ef2fd7", "@type": "uco-observable:Message", "olo:length": null, "olo:slot": null, "uco-core:hasFacet": [ { - "@id": "5fe1b398-6cad-471e-9554-b136721fc50b", + "@id": "551338f3-0bb6-5325-a63a-f164f8750a17", "@type": "uco-observable:MessageFacet" } ] }, { - "@id": "8023fd10-14f9-48f9-adeb-5b8cc863c44b", + "@id": "09b10c04-d04b-5808-a1b6-0392dd3a00e8", "@type": "uco-observable:Message", "olo:length": null, "olo:slot": null, "uco-core:hasFacet": [ { - "@id": "83abaf69-f26e-4d0d-bd7d-49667e9e2372", + "@id": "a19e1e1f-3953-5fb9-92b6-2b46f85752b2", "@type": "uco-observable:MessageFacet" } ] }, { - "@id": "72fb1f29-fc05-4156-9716-faf497fb9384", + "@id": "562c20c5-bef7-5919-a969-6c820a80e0d3", "@type": "uco-observable:MessageThread", "uco-core:hasFacet": [ { - "@id": "1253a5d9-7cb6-4114-945e-e9d6bbde4b85", + "@id": "6f79d4ae-d92c-5cad-bbe5-a0afde6f475a", "@type": "uco-observable:MessageThreadFacet", "uco-observable:displayName": "some name", "uco-observable:messageThread": { - "@id": "0189faff-1edd-4d62-a791-8de0531c2255", + "@id": "56f74818-1d3d-51f9-8cb1-d6bdc8ecee60", "@type": "uco-types:Thread", "co:size": { "@type": "xsd:nonNegativeInteger", @@ -666,13 +666,13 @@ }, "co:element": [ { - "@id": "03c4a175-2baf-4346-bf47-c96f2ab558cd" + "@id": "09b10c04-d04b-5808-a1b6-0392dd3a00e8" }, { - "@id": "49934f4b-6eb8-464c-9306-e039c8850c71" + "@id": "7c4d5cff-deb0-5031-b881-c572d6ef2fd7" }, { - "@id": "8023fd10-14f9-48f9-adeb-5b8cc863c44b" + "@id": "bb9cd337-eb51-50dd-9cee-00f9e9acbf94" } ] } From 491c4f8df821ad5531768c00da9cffd4a5317cd7 Mon Sep 17 00:00:00 2001 From: Alex Nelson Date: Tue, 12 Mar 2024 11:43:19 -0400 Subject: [PATCH 22/22] Integrate mix_utils directory into package The migrated test script demonstrates the module import pattern for consumers. Signed-off-by: Alex Nelson --- mix_utils/utils.py => case_mapping/mix_utils.py | 0 {mix_utils => tests}/test_duplicate.py | 10 +++++----- 2 files changed, 5 insertions(+), 5 deletions(-) rename mix_utils/utils.py => case_mapping/mix_utils.py (100%) rename {mix_utils => tests}/test_duplicate.py (95%) diff --git a/mix_utils/utils.py b/case_mapping/mix_utils.py similarity index 100% rename from mix_utils/utils.py rename to case_mapping/mix_utils.py diff --git a/mix_utils/test_duplicate.py b/tests/test_duplicate.py similarity index 95% rename from mix_utils/test_duplicate.py rename to tests/test_duplicate.py index 32bd7be..aed691d 100644 --- a/mix_utils/test_duplicate.py +++ b/tests/test_duplicate.py @@ -1,14 +1,14 @@ import uuid from typing import Any, Union -import utils +from case_mapping import mix_utils def check_app_name( app_name: str, app_names: list[str], app_objects: list[dict[str, Any]], uuid: str ) -> dict[str, Any]: - # c_check = utils.CheckDuplicate() - observable_app = utils.check_value( + # c_check = mix_utils.CheckDuplicate() + observable_app = mix_utils.check_value( app_name, uuid, value=app_name, @@ -26,8 +26,8 @@ def check_geo_coordinates( geo_objects: list[dict[str, Any]], uuid: str, ) -> dict[str, Any]: - # c_check = utils.CheckDuplicate() - observable_app = utils.check_value( + # c_check = mix_utils.CheckDuplicate() + observable_app = mix_utils.check_value( latitude, longitude, uuid,