├── .gitattributes ├── .github └── FUNDING.yml ├── .gitignore ├── GraphSpy ├── GraphSpy.py ├── __init__.py ├── static │ ├── css │ │ └── style.css │ └── js │ │ ├── functions.js │ │ ├── purify.min.js │ │ └── theme.js ├── templates │ ├── OneDrive.html │ ├── SharePoint.html │ ├── SharePointDrives.html │ ├── SharePointSites.html │ ├── access_token_modal.html │ ├── access_tokens.html │ ├── custom_requests.html │ ├── device_codes.html │ ├── entra_users.html │ ├── generic_search.html │ ├── index.html │ ├── layout.html │ ├── mfa.html │ ├── outlook.html │ ├── outlook_graph.html │ ├── recent_files.html │ ├── refresh_token_modal.html │ ├── refresh_tokens.html │ ├── settings.html │ ├── shared_with_me.html │ └── teams.html └── version.txt ├── LICENSE.txt ├── MANIFEST.in ├── README.md ├── images ├── access_tokens_1.png ├── access_tokens_2.png ├── custom_request_templates.png ├── custom_requests.png ├── custom_requests_2.png ├── device_codes.png ├── entra_users_custom_properties.png ├── entra_users_details_1.png ├── entra_users_details_2.png ├── entra_users_details_3.png ├── entra_users_membership_count.png ├── entra_users_overview.png ├── files_shared_with_me.png ├── graph_search_1.png ├── graph_search_2.png ├── mfa_methods_fido.png ├── mfa_methods_mobile.png ├── mfa_methods_ms_app.png ├── mfa_methods_overview.png ├── ms_teams.png ├── onedrive_1.png ├── onedrive_2.png ├── outlook_1.png ├── outlook_2.png ├── outlook_graph_overview.png ├── outlook_graph_send_email.png ├── recent_files.png ├── refresh_tokens.png ├── settings.png ├── sharepoint_drives.png ├── sharepoint_files.png ├── sharepoint_sites.png ├── token_side_bar_1.png └── token_side_bar_2.png ├── requirements.txt └── setup.py /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: RedByte1337 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__/ 2 | .vs/ 3 | dist/ 4 | .vscode/ 5 | .cursor/ 6 | location.txt 7 | *.code-workspace 8 | -------------------------------------------------------------------------------- /GraphSpy/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RedByte1337/GraphSpy/56912ad634e0f5379a35b79c90dd97a32e5733d2/GraphSpy/__init__.py -------------------------------------------------------------------------------- /GraphSpy/static/css/style.css: -------------------------------------------------------------------------------- 1 | /* Vertical align all text in datatables */ 2 | table.dataTable tbody tr td { 3 | vertical-align: middle; 4 | } 5 | /* Center action icons used in datatables */ 6 | td:has(> i.fi), td.dt-control { 7 | text-align: center; 8 | } 9 | /* Change the color of the icons on hover */ 10 | .icon-hover-change:hover { 11 | color: var(--bs-primary-border-subtle); 12 | } 13 | /* Better background for datatables copy notifications */ 14 | div.dt-button-info#datatables_buttons_info { 15 | background: var(--bs-secondary-bg); 16 | } 17 | /* Teams CSS */ 18 | #teams_message_container pre { 19 | background: var(--bs-secondary-bg); 20 | border-radius: var(--bs-border-radius-sm); 21 | padding-left:.25rem; 22 | } 23 | #teams_message_container td { 24 | border-width: 1px; 25 | border-color: var(--bs-secondary-border-subtle); 26 | padding: .25rem; 27 | } 28 | #teams_message_container .card-body p { 29 | margin-bottom: .25rem; 30 | } 31 | blockquote { 32 | border-radius: var(--bs-border-radius-lg); 33 | border: var(--bs-border-width) var(--bs-border-style) var(--bs-border-color); 34 | padding: .5rem; 35 | } 36 | 37 | /* JSON Styling */ 38 | :not(pre) > code[class*=language-], pre[class*=language-] { 39 | background: transparent !important; 40 | } 41 | 42 | .token.operator, .token.punctuation { 43 | color: var(--bs-body-color); 44 | } 45 | 46 | .token { 47 | text-shadow: none !important; 48 | } 49 | 50 | /* Bootstrap negative margins */ 51 | 52 | .m-n1 { margin: -0.25rem; } 53 | .mt-n1 { margin-top: -0.25rem; } 54 | .mb-n1 { margin-bottom: -0.25rem; } 55 | .ms-n1 { margin-left: -0.25rem; } 56 | .me-n1 { margin-right: -0.25rem; } 57 | .mx-n1 { margin-left: -0.25rem; margin-right: -0.25rem; } 58 | .my-n1 { margin-top: -0.25rem; margin-bottom: -0.25rem; } 59 | 60 | .m-n2 { margin: -0.5rem; } 61 | .mt-n2 { margin-top: -0.5rem; } 62 | .mb-n2 { margin-bottom: -0.5rem; } 63 | .ms-n2 { margin-left: -0.5rem; } 64 | .me-n2 { margin-right: -0.5rem; } 65 | .mx-n2 { margin-left: -0.5rem; margin-right: -0.5rem; } 66 | .my-n2 { margin-top: -0.5rem; margin-bottom: -0.5rem; } 67 | 68 | .m-n3 { margin: -1rem; } 69 | .mt-n3 { margin-top: -1rem; } 70 | .mb-n3 { margin-bottom: -1rem; } 71 | .ms-n3 { margin-left: -1rem; } 72 | .me-n3 { margin-right: -1rem; } 73 | .mx-n3 { margin-left: -1rem; margin-right: -1rem; } 74 | .my-n3 { margin-top: -1rem; margin-bottom: -1rem; } 75 | 76 | .m-n4 { margin: -1.5rem; } 77 | .mt-n4 { margin-top: -1.5rem; } 78 | .mb-n4 { margin-bottom: -1.5rem; } 79 | .ms-n4 { margin-left: -1.5rem; } 80 | .me-n4 { margin-right: -1.5rem; } 81 | .mx-n4 { margin-left: -1.5rem; margin-right: -1.5rem; } 82 | .my-n4 { margin-top: -1.5rem; margin-bottom: -1.5rem; } 83 | 84 | .m-n5 { margin: -3rem; } 85 | .mt-n5 { margin-top: -3rem; } 86 | .mb-n5 { margin-bottom: -3rem; } 87 | .ms-n5 { margin-left: -3rem; } 88 | .me-n5 { margin-right: -3rem; } 89 | .mx-n5 { margin-left: -3rem; margin-right: -3rem; } 90 | .my-n5 { margin-top: -3rem; margin-bottom: -3rem; } -------------------------------------------------------------------------------- /GraphSpy/static/js/purify.min.js: -------------------------------------------------------------------------------- 1 | /*! @license DOMPurify 3.1.1 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.1.1/LICENSE */ 2 | !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).DOMPurify=t()}(this,(function(){"use strict";const{entries:e,setPrototypeOf:t,isFrozen:n,getPrototypeOf:o,getOwnPropertyDescriptor:r}=Object;let{freeze:i,seal:a,create:l}=Object,{apply:c,construct:s}="undefined"!=typeof Reflect&&Reflect;i||(i=function(e){return e}),a||(a=function(e){return e}),c||(c=function(e,t,n){return e.apply(t,n)}),s||(s=function(e,t){return new e(...t)});const u=b(Array.prototype.forEach),m=b(Array.prototype.pop),p=b(Array.prototype.push),f=b(String.prototype.toLowerCase),d=b(String.prototype.toString),h=b(String.prototype.match),g=b(String.prototype.replace),T=b(String.prototype.indexOf),_=b(String.prototype.trim),y=b(Object.prototype.hasOwnProperty),E=b(RegExp.prototype.test),A=(N=TypeError,function(){for(var e=arguments.length,t=new Array(e),n=0;n1?n-1:0),r=1;r2&&void 0!==arguments[2]?arguments[2]:f;t&&t(e,null);let i=o.length;for(;i--;){let t=o[i];if("string"==typeof t){const e=r(t);e!==t&&(n(o)||(o[i]=e),t=e)}e[t]=!0}return e}function R(e){for(let t=0;t/gm),B=a(/\${[\w\W]*}/gm),W=a(/^data-[\-\w.\u00B7-\uFFFF]/),G=a(/^aria-[\-\w]+$/),Y=a(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i),j=a(/^(?:\w+script|data):/i),X=a(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g),q=a(/^html$/i),$=a(/^[a-z][.\w]*(-[.\w]+)+$/i);var K=Object.freeze({__proto__:null,MUSTACHE_EXPR:H,ERB_EXPR:z,TMPLIT_EXPR:B,DATA_ATTR:W,ARIA_ATTR:G,IS_ALLOWED_URI:Y,IS_SCRIPT_OR_DATA:j,ATTR_WHITESPACE:X,DOCTYPE_NAME:q,CUSTOM_ELEMENT:$});const V=function(){return"undefined"==typeof window?null:window},Z=function(e,t){if("object"!=typeof e||"function"!=typeof e.createPolicy)return null;let n=null;const o="data-tt-policy-suffix";t&&t.hasAttribute(o)&&(n=t.getAttribute(o));const r="dompurify"+(n?"#"+n:"");try{return e.createPolicy(r,{createHTML:e=>e,createScriptURL:e=>e})}catch(e){return console.warn("TrustedTypes policy "+r+" could not be created."),null}};var J=function t(){let n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:V();const o=e=>t(e);if(o.version="3.1.1",o.removed=[],!n||!n.document||9!==n.document.nodeType)return o.isSupported=!1,o;let{document:r}=n;const a=r,c=a.currentScript,{DocumentFragment:s,HTMLTemplateElement:N,Node:b,Element:R,NodeFilter:H,NamedNodeMap:z=n.NamedNodeMap||n.MozNamedAttrMap,HTMLFormElement:B,DOMParser:W,trustedTypes:G}=n,j=R.prototype,X=C(j,"cloneNode"),$=C(j,"nextSibling"),J=C(j,"childNodes"),Q=C(j,"parentNode");if("function"==typeof N){const e=r.createElement("template");e.content&&e.content.ownerDocument&&(r=e.content.ownerDocument)}let ee,te="";const{implementation:ne,createNodeIterator:oe,createDocumentFragment:re,getElementsByTagName:ie}=r,{importNode:ae}=a;let le={};o.isSupported="function"==typeof e&&"function"==typeof Q&&ne&&void 0!==ne.createHTMLDocument;const{MUSTACHE_EXPR:ce,ERB_EXPR:se,TMPLIT_EXPR:ue,DATA_ATTR:me,ARIA_ATTR:pe,IS_SCRIPT_OR_DATA:fe,ATTR_WHITESPACE:de,CUSTOM_ELEMENT:he}=K;let{IS_ALLOWED_URI:ge}=K,Te=null;const _e=S({},[...v,...L,...D,...x,...M]);let ye=null;const Ee=S({},[...I,...U,...P,...F]);let Ae=Object.seal(l(null,{tagNameCheck:{writable:!0,configurable:!1,enumerable:!0,value:null},attributeNameCheck:{writable:!0,configurable:!1,enumerable:!0,value:null},allowCustomizedBuiltInElements:{writable:!0,configurable:!1,enumerable:!0,value:!1}})),Ne=null,be=null,Se=!0,Re=!0,we=!1,Ce=!0,ve=!1,Le=!0,De=!1,Oe=!1,xe=!1,ke=!1,Me=!1,Ie=!1,Ue=!0,Pe=!1;const Fe="user-content-";let He=!0,ze=!1,Be={},We=null;const Ge=S({},["annotation-xml","audio","colgroup","desc","foreignobject","head","iframe","math","mi","mn","mo","ms","mtext","noembed","noframes","noscript","plaintext","script","style","svg","template","thead","title","video","xmp"]);let Ye=null;const je=S({},["audio","video","img","source","image","track"]);let Xe=null;const qe=S({},["alt","class","for","id","label","name","pattern","placeholder","role","summary","title","value","style","xmlns"]),$e="http://www.w3.org/1998/Math/MathML",Ke="http://www.w3.org/2000/svg",Ve="http://www.w3.org/1999/xhtml";let Ze=Ve,Je=!1,Qe=null;const et=S({},[$e,Ke,Ve],d);let tt=null;const nt=["application/xhtml+xml","text/html"],ot="text/html";let rt=null,it=null;const at=255,lt=r.createElement("form"),ct=function(e){return e instanceof RegExp||e instanceof Function},st=function(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};if(!it||it!==e){if(e&&"object"==typeof e||(e={}),e=w(e),tt=-1===nt.indexOf(e.PARSER_MEDIA_TYPE)?ot:e.PARSER_MEDIA_TYPE,rt="application/xhtml+xml"===tt?d:f,Te=y(e,"ALLOWED_TAGS")?S({},e.ALLOWED_TAGS,rt):_e,ye=y(e,"ALLOWED_ATTR")?S({},e.ALLOWED_ATTR,rt):Ee,Qe=y(e,"ALLOWED_NAMESPACES")?S({},e.ALLOWED_NAMESPACES,d):et,Xe=y(e,"ADD_URI_SAFE_ATTR")?S(w(qe),e.ADD_URI_SAFE_ATTR,rt):qe,Ye=y(e,"ADD_DATA_URI_TAGS")?S(w(je),e.ADD_DATA_URI_TAGS,rt):je,We=y(e,"FORBID_CONTENTS")?S({},e.FORBID_CONTENTS,rt):Ge,Ne=y(e,"FORBID_TAGS")?S({},e.FORBID_TAGS,rt):{},be=y(e,"FORBID_ATTR")?S({},e.FORBID_ATTR,rt):{},Be=!!y(e,"USE_PROFILES")&&e.USE_PROFILES,Se=!1!==e.ALLOW_ARIA_ATTR,Re=!1!==e.ALLOW_DATA_ATTR,we=e.ALLOW_UNKNOWN_PROTOCOLS||!1,Ce=!1!==e.ALLOW_SELF_CLOSE_IN_ATTR,ve=e.SAFE_FOR_TEMPLATES||!1,Le=!1!==e.SAFE_FOR_XML,De=e.WHOLE_DOCUMENT||!1,ke=e.RETURN_DOM||!1,Me=e.RETURN_DOM_FRAGMENT||!1,Ie=e.RETURN_TRUSTED_TYPE||!1,xe=e.FORCE_BODY||!1,Ue=!1!==e.SANITIZE_DOM,Pe=e.SANITIZE_NAMED_PROPS||!1,He=!1!==e.KEEP_CONTENT,ze=e.IN_PLACE||!1,ge=e.ALLOWED_URI_REGEXP||Y,Ze=e.NAMESPACE||Ve,Ae=e.CUSTOM_ELEMENT_HANDLING||{},e.CUSTOM_ELEMENT_HANDLING&&ct(e.CUSTOM_ELEMENT_HANDLING.tagNameCheck)&&(Ae.tagNameCheck=e.CUSTOM_ELEMENT_HANDLING.tagNameCheck),e.CUSTOM_ELEMENT_HANDLING&&ct(e.CUSTOM_ELEMENT_HANDLING.attributeNameCheck)&&(Ae.attributeNameCheck=e.CUSTOM_ELEMENT_HANDLING.attributeNameCheck),e.CUSTOM_ELEMENT_HANDLING&&"boolean"==typeof e.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements&&(Ae.allowCustomizedBuiltInElements=e.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements),ve&&(Re=!1),Me&&(ke=!0),Be&&(Te=S({},M),ye=[],!0===Be.html&&(S(Te,v),S(ye,I)),!0===Be.svg&&(S(Te,L),S(ye,U),S(ye,F)),!0===Be.svgFilters&&(S(Te,D),S(ye,U),S(ye,F)),!0===Be.mathMl&&(S(Te,x),S(ye,P),S(ye,F))),e.ADD_TAGS&&(Te===_e&&(Te=w(Te)),S(Te,e.ADD_TAGS,rt)),e.ADD_ATTR&&(ye===Ee&&(ye=w(ye)),S(ye,e.ADD_ATTR,rt)),e.ADD_URI_SAFE_ATTR&&S(Xe,e.ADD_URI_SAFE_ATTR,rt),e.FORBID_CONTENTS&&(We===Ge&&(We=w(We)),S(We,e.FORBID_CONTENTS,rt)),He&&(Te["#text"]=!0),De&&S(Te,["html","head","body"]),Te.table&&(S(Te,["tbody"]),delete Ne.tbody),e.TRUSTED_TYPES_POLICY){if("function"!=typeof e.TRUSTED_TYPES_POLICY.createHTML)throw A('TRUSTED_TYPES_POLICY configuration option must provide a "createHTML" hook.');if("function"!=typeof e.TRUSTED_TYPES_POLICY.createScriptURL)throw A('TRUSTED_TYPES_POLICY configuration option must provide a "createScriptURL" hook.');ee=e.TRUSTED_TYPES_POLICY,te=ee.createHTML("")}else void 0===ee&&(ee=Z(G,c)),null!==ee&&"string"==typeof te&&(te=ee.createHTML(""));i&&i(e),it=e}},ut=S({},["mi","mo","mn","ms","mtext"]),mt=S({},["foreignobject","desc","title","annotation-xml"]),pt=S({},["title","style","font","a","script"]),ft=S({},[...L,...D,...O]),dt=S({},[...x,...k]),ht=function(e){let t=Q(e);t&&t.tagName||(t={namespaceURI:Ze,tagName:"template"});const n=f(e.tagName),o=f(t.tagName);return!!Qe[e.namespaceURI]&&(e.namespaceURI===Ke?t.namespaceURI===Ve?"svg"===n:t.namespaceURI===$e?"svg"===n&&("annotation-xml"===o||ut[o]):Boolean(ft[n]):e.namespaceURI===$e?t.namespaceURI===Ve?"math"===n:t.namespaceURI===Ke?"math"===n&&mt[o]:Boolean(dt[n]):e.namespaceURI===Ve?!(t.namespaceURI===Ke&&!mt[o])&&(!(t.namespaceURI===$e&&!ut[o])&&(!dt[n]&&(pt[n]||!ft[n]))):!("application/xhtml+xml"!==tt||!Qe[e.namespaceURI]))},gt=function(e){p(o.removed,{element:e});try{e.parentNode.removeChild(e)}catch(t){e.remove()}},Tt=function(e,t){try{p(o.removed,{attribute:t.getAttributeNode(e),from:t})}catch(e){p(o.removed,{attribute:null,from:t})}if(t.removeAttribute(e),"is"===e&&!ye[e])if(ke||Me)try{gt(t)}catch(e){}else try{t.setAttribute(e,"")}catch(e){}},_t=function(e){let t=null,n=null;if(xe)e=""+e;else{const t=h(e,/^[\r\n\t ]+/);n=t&&t[0]}"application/xhtml+xml"===tt&&Ze===Ve&&(e=''+e+"");const o=ee?ee.createHTML(e):e;if(Ze===Ve)try{t=(new W).parseFromString(o,tt)}catch(e){}if(!t||!t.documentElement){t=ne.createDocument(Ze,"template",null);try{t.documentElement.innerHTML=Je?te:o}catch(e){}}const i=t.body||t.documentElement;return e&&n&&i.insertBefore(r.createTextNode(n),i.childNodes[0]||null),Ze===Ve?ie.call(t,De?"html":"body")[0]:De?t.documentElement:i},yt=function(e){return oe.call(e.ownerDocument||e,e,H.SHOW_ELEMENT|H.SHOW_COMMENT|H.SHOW_TEXT|H.SHOW_PROCESSING_INSTRUCTION|H.SHOW_CDATA_SECTION,null)},Et=function(e){return e instanceof B&&(void 0!==e.__depth&&"number"!=typeof e.__depth||void 0!==e.__removalCount&&"number"!=typeof e.__removalCount||"string"!=typeof e.nodeName||"string"!=typeof e.textContent||"function"!=typeof e.removeChild||!(e.attributes instanceof z)||"function"!=typeof e.removeAttribute||"function"!=typeof e.setAttribute||"string"!=typeof e.namespaceURI||"function"!=typeof e.insertBefore||"function"!=typeof e.hasChildNodes)},At=function(e){return"function"==typeof b&&e instanceof b},Nt=function(e,t,n){le[e]&&u(le[e],(e=>{e.call(o,t,n,it)}))},bt=function(e){let t=null;if(Nt("beforeSanitizeElements",e,null),Et(e))return gt(e),!0;const n=rt(e.nodeName);if(Nt("uponSanitizeElement",e,{tagName:n,allowedTags:Te}),e.hasChildNodes()&&!At(e.firstElementChild)&&E(/<[/\w]/g,e.innerHTML)&&E(/<[/\w]/g,e.textContent))return gt(e),!0;if(7===e.nodeType)return gt(e),!0;if(Le&&8===e.nodeType&&E(/<[/\w]/g,e.data))return gt(e),!0;if(!Te[n]||Ne[n]){if(!Ne[n]&&Rt(n)){if(Ae.tagNameCheck instanceof RegExp&&E(Ae.tagNameCheck,n))return!1;if(Ae.tagNameCheck instanceof Function&&Ae.tagNameCheck(n))return!1}if(He&&!We[n]){const t=Q(e)||e.parentNode,n=J(e)||e.childNodes;if(n&&t){for(let o=n.length-1;o>=0;--o){const r=X(n[o],!0);r.__removalCount=(e.__removalCount||0)+1,t.insertBefore(r,$(e))}}}return gt(e),!0}return e instanceof R&&!ht(e)?(gt(e),!0):"noscript"!==n&&"noembed"!==n&&"noframes"!==n||!E(/<\/no(script|embed|frames)/i,e.innerHTML)?(ve&&3===e.nodeType&&(t=e.textContent,u([ce,se,ue],(e=>{t=g(t,e," ")})),e.textContent!==t&&(p(o.removed,{element:e.cloneNode()}),e.textContent=t)),Nt("afterSanitizeElements",e,null),!1):(gt(e),!0)},St=function(e,t,n){if(Ue&&("id"===t||"name"===t)&&(n in r||n in lt))return!1;if(Re&&!be[t]&&E(me,t));else if(Se&&E(pe,t));else if(!ye[t]||be[t]){if(!(Rt(e)&&(Ae.tagNameCheck instanceof RegExp&&E(Ae.tagNameCheck,e)||Ae.tagNameCheck instanceof Function&&Ae.tagNameCheck(e))&&(Ae.attributeNameCheck instanceof RegExp&&E(Ae.attributeNameCheck,t)||Ae.attributeNameCheck instanceof Function&&Ae.attributeNameCheck(t))||"is"===t&&Ae.allowCustomizedBuiltInElements&&(Ae.tagNameCheck instanceof RegExp&&E(Ae.tagNameCheck,n)||Ae.tagNameCheck instanceof Function&&Ae.tagNameCheck(n))))return!1}else if(Xe[t]);else if(E(ge,g(n,de,"")));else if("src"!==t&&"xlink:href"!==t&&"href"!==t||"script"===e||0!==T(n,"data:")||!Ye[e]){if(we&&!E(fe,g(n,de,"")));else if(n)return!1}else;return!0},Rt=function(e){return"annotation-xml"!==e&&h(e,he)},wt=function(e){Nt("beforeSanitizeAttributes",e,null);const{attributes:t}=e;if(!t)return;const n={attrName:"",attrValue:"",keepAttr:!0,allowedAttributes:ye};let r=t.length;for(;r--;){const i=t[r],{name:a,namespaceURI:l,value:c}=i,s=rt(a);let p="value"===a?c:_(c);if(n.attrName=s,n.attrValue=p,n.keepAttr=!0,n.forceKeepAttr=void 0,Nt("uponSanitizeAttribute",e,n),p=n.attrValue,n.forceKeepAttr)continue;if(Tt(a,e),!n.keepAttr)continue;if(!Ce&&E(/\/>/i,p)){Tt(a,e);continue}ve&&u([ce,se,ue],(e=>{p=g(p,e," ")}));const f=rt(e.nodeName);if(St(f,s,p)){if(!Pe||"id"!==s&&"name"!==s||(Tt(a,e),p=Fe+p),ee&&"object"==typeof G&&"function"==typeof G.getAttributeType)if(l);else switch(G.getAttributeType(f,s)){case"TrustedHTML":p=ee.createHTML(p);break;case"TrustedScriptURL":p=ee.createScriptURL(p)}try{l?e.setAttributeNS(l,a,p):e.setAttribute(a,p),m(o.removed)}catch(e){}}}Nt("afterSanitizeAttributes",e,null)},Ct=function e(t){let n=null;const o=yt(t);for(Nt("beforeSanitizeShadowDOM",t,null);n=o.nextNode();){if(Nt("uponSanitizeShadowNode",n,null),bt(n))continue;const t=Q(n);1===n.nodeType&&(t&&t.__depth?n.__depth=(n.__removalCount||0)+t.__depth+1:n.__depth=1),n.__depth>=at&>(n),n.content instanceof s&&(n.content.__depth=n.__depth,e(n.content)),wt(n)}Nt("afterSanitizeShadowDOM",t,null)};return o.sanitize=function(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=null,r=null,i=null,l=null;if(Je=!e,Je&&(e="\x3c!--\x3e"),"string"!=typeof e&&!At(e)){if("function"!=typeof e.toString)throw A("toString is not a function");if("string"!=typeof(e=e.toString()))throw A("dirty is not a string, aborting")}if(!o.isSupported)return e;if(Oe||st(t),o.removed=[],"string"==typeof e&&(ze=!1),ze){if(e.nodeName){const t=rt(e.nodeName);if(!Te[t]||Ne[t])throw A("root node is forbidden and cannot be sanitized in-place")}}else if(e instanceof b)n=_t("\x3c!----\x3e"),r=n.ownerDocument.importNode(e,!0),1===r.nodeType&&"BODY"===r.nodeName||"HTML"===r.nodeName?n=r:n.appendChild(r);else{if(!ke&&!ve&&!De&&-1===e.indexOf("<"))return ee&&Ie?ee.createHTML(e):e;if(n=_t(e),!n)return ke?null:Ie?te:""}n&&xe&>(n.firstChild);const c=yt(ze?e:n);for(;i=c.nextNode();){if(bt(i))continue;const e=Q(i);1===i.nodeType&&(e&&e.__depth?i.__depth=(i.__removalCount||0)+e.__depth+1:i.__depth=1),i.__depth>=at&>(i),i.content instanceof s&&(i.content.__depth=i.__depth,Ct(i.content)),wt(i)}if(ze)return e;if(ke){if(Me)for(l=re.call(n.ownerDocument);n.firstChild;)l.appendChild(n.firstChild);else l=n;return(ye.shadowroot||ye.shadowrootmode)&&(l=ae.call(a,l,!0)),l}let m=De?n.outerHTML:n.innerHTML;return De&&Te["!doctype"]&&n.ownerDocument&&n.ownerDocument.doctype&&n.ownerDocument.doctype.name&&E(q,n.ownerDocument.doctype.name)&&(m="\n"+m),ve&&u([ce,se,ue],(e=>{m=g(m,e," ")})),ee&&Ie?ee.createHTML(m):m},o.setConfig=function(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};st(e),Oe=!0},o.clearConfig=function(){it=null,Oe=!1},o.isValidAttribute=function(e,t,n){it||st({});const o=rt(e),r=rt(t);return St(o,r,n)},o.addHook=function(e,t){"function"==typeof t&&(le[e]=le[e]||[],p(le[e],t))},o.removeHook=function(e){if(le[e])return m(le[e])},o.removeHooks=function(e){le[e]&&(le[e]=[])},o.removeAllHooks=function(){le={}},o}();return J})); 3 | //# sourceMappingURL=purify.min.js.map 4 | -------------------------------------------------------------------------------- /GraphSpy/static/js/theme.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Color mode toggler for Bootstrap's docs (https://getbootstrap.com/) 3 | * Copyright 2011-2023 The Bootstrap Authors 4 | * Licensed under the Creative Commons Attribution 3.0 Unported License. 5 | */ 6 | 7 | (() => { 8 | 'use strict' 9 | 10 | const getStoredTheme = () => localStorage.getItem('theme') 11 | const setStoredTheme = theme => localStorage.setItem('theme', theme) 12 | 13 | const getPreferredTheme = () => { 14 | const storedTheme = getStoredTheme() 15 | if (storedTheme) { 16 | return storedTheme 17 | } 18 | // Return dark theme by default 19 | return "dark" 20 | } 21 | 22 | const setTheme = theme => { 23 | if (theme === 'auto' && window.matchMedia('(prefers-color-scheme: dark)').matches) { 24 | document.documentElement.setAttribute('data-bs-theme', 'dark') 25 | } else { 26 | document.documentElement.setAttribute('data-bs-theme', theme) 27 | } 28 | } 29 | 30 | setTheme(getPreferredTheme()) 31 | 32 | const showActiveTheme = (theme, focus = false) => { 33 | const themeSwitcher = document.querySelector('#bd-theme') 34 | 35 | if (!themeSwitcher) { 36 | return 37 | } 38 | 39 | const btnToActive = document.querySelector(`[data-bs-theme-value="${theme}"]`) 40 | 41 | document.querySelectorAll('[data-bs-theme-value]').forEach(element => { 42 | element.classList.remove('active') 43 | element.setAttribute('aria-pressed', 'false') 44 | }) 45 | 46 | btnToActive.classList.add('active') 47 | btnToActive.setAttribute('aria-pressed', 'true') 48 | 49 | if (focus) { 50 | themeSwitcher.focus() 51 | } 52 | } 53 | 54 | window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => { 55 | const storedTheme = getStoredTheme() 56 | if (storedTheme !== 'light' && storedTheme !== 'dark') { 57 | setTheme(getPreferredTheme()) 58 | } 59 | }) 60 | 61 | window.addEventListener('DOMContentLoaded', () => { 62 | showActiveTheme(getPreferredTheme()) 63 | 64 | document.querySelectorAll('[data-bs-theme-value]') 65 | .forEach(toggle => { 66 | toggle.addEventListener('click', () => { 67 | const theme = toggle.getAttribute('data-bs-theme-value') 68 | setStoredTheme(theme) 69 | setTheme(theme) 70 | showActiveTheme(theme, true) 71 | }) 72 | }) 73 | }) 74 | })() -------------------------------------------------------------------------------- /GraphSpy/templates/OneDrive.html: -------------------------------------------------------------------------------- 1 | {% extends 'layout.html'%} 2 | 3 | {%block content%} 4 | 5 |
6 |
7 |
8 |

OneDrive Files

9 |
10 |
11 | 12 | 13 | 14 |
15 |
16 |
17 | 18 | 19 | 20 |
21 |
22 |
23 | 24 |
25 |
26 | 29 |
30 |
31 |

Upload file

32 |
33 |
34 | 35 | 39 |
40 |
41 |
42 |
43 |
44 |
45 |

Files

46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 |
CreatedLast ModifiedFile NameFile SizeURL
60 |
61 | 62 | 235 | {%endblock content%} 236 | -------------------------------------------------------------------------------- /GraphSpy/templates/SharePoint.html: -------------------------------------------------------------------------------- 1 | {% extends 'layout.html'%} 2 | 3 | {%block content%} 4 | 5 |
6 |
7 |
8 |

SharePoint Files

9 |
10 |
11 | 12 | 13 |
14 |
15 | 16 | 17 | 18 |
19 |
20 |
21 | 22 | 23 | 24 |
25 |
26 |
27 | 28 |
29 |
30 | 33 |
34 |
35 |

Upload file

36 |
37 |
38 | 39 | 43 |
44 |
45 |
46 |
47 |
48 | 49 |
50 |

Files Table

51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 |
CreatedLast ModifiedFile NameFile SizeURL
65 |
66 | 253 | {%endblock content%} -------------------------------------------------------------------------------- /GraphSpy/templates/SharePointDrives.html: -------------------------------------------------------------------------------- 1 | {% extends 'layout.html'%} 2 | 3 | {%block content%} 4 | 5 |
6 |
7 |

SharePoint Drives

8 |
9 |
10 | 11 | 12 |
13 |
14 | 15 |
16 | 17 | 18 |
19 |
20 |
21 | 22 |
23 |
24 | 27 |
28 |
29 | 30 |
31 |

Drives Table

32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 |
CreatedLast ModifiedDrive NameURL
44 |
45 | 139 | {%endblock content%} -------------------------------------------------------------------------------- /GraphSpy/templates/SharePointSites.html: -------------------------------------------------------------------------------- 1 | {% extends 'layout.html'%} 2 | 3 | {%block content%} 4 | 5 |
6 |
7 |

SharePoint Sites

8 |
9 |
10 | 11 | 12 |
13 |
14 | 15 | 16 | Max 500 17 |
18 |
19 | 20 |
21 | 22 | 23 |
24 |
25 |
26 | 27 |
28 |
29 | 32 |
33 |
34 | 35 |
36 |

Sites Table

37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 |
CreatedLast ModifiedSite NameDisplay NameURL
50 |
51 | 144 | {%endblock content%} -------------------------------------------------------------------------------- /GraphSpy/templates/access_token_modal.html: -------------------------------------------------------------------------------- 1 | 2 | 38 | -------------------------------------------------------------------------------- /GraphSpy/templates/access_tokens.html: -------------------------------------------------------------------------------- 1 | {% extends 'layout.html'%} 2 | 3 | {%block content%} 4 | 5 |
6 |
7 |
8 |
9 |

Add Access Token

10 |
11 |
12 | 13 | 14 |
15 |
16 | 17 | 18 |
19 |
20 | 21 |
22 |
23 |
24 |
25 |
26 |

Active Access Token

27 |
28 |
29 | 30 |
31 |
32 | 33 |
34 |
35 | 36 |
37 |
38 |
39 |
40 |
41 |
42 |

Refresh To Access Token

43 |
44 |
45 |
46 | API Version 47 | 48 | 49 | 50 | 51 |
52 |
53 |
54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 |
66 | 79 |
80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 |
93 |
94 |
95 | 96 | 97 |
98 |
99 | 100 | 101 |
102 |
103 |
104 | 105 | 106 |
107 |
108 | 109 |
110 |
111 |
112 |
113 |
114 | 128 |
129 |
130 |

Access Tokens

131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 |
IDIssuedExpiresUserResourceDescription
147 |
148 | 256 | 257 | {%endblock content%} -------------------------------------------------------------------------------- /GraphSpy/templates/custom_requests.html: -------------------------------------------------------------------------------- 1 | {% extends 'layout.html'%} 2 | 3 | {%block content%} 4 | 5 |
6 |
7 |

Custom Requests

8 |
9 |
10 | 11 | 20 |
21 |
22 | 23 | 29 |
30 |
31 | 32 |
33 | 34 | 35 |
36 |
37 |
38 |
39 | 40 |
41 | 42 | 43 | 44 |
45 |
46 |
47 | 48 | 49 |
50 |
51 | 52 | 53 |
54 |
55 |
56 | Header 57 | 58 | 59 | 60 |
61 |
62 |
63 |
64 | Variable 65 | 66 | 67 | 68 |
69 |
70 |
71 | 72 | 73 |
74 |
75 | 76 | 77 |
78 |
79 | 83 |
84 |
85 | 88 |
89 |
90 | 119 | 120 | 301 | 302 | 303 | 337 | 449 | 450 | {%endblock content%} 451 | -------------------------------------------------------------------------------- /GraphSpy/templates/device_codes.html: -------------------------------------------------------------------------------- 1 | {% extends 'layout.html'%} 2 | 3 | {%block content%} 4 | 5 |
6 |

Generate Device Code

7 |
8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 |
21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 |
34 |
35 | 36 | 37 |
38 |
39 | 40 |
41 |
42 |
43 | 46 |
47 |
48 |

Device Code List

49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 |
IDGenerated AtExpires AtLast Polled AtUser CodeClient IDStatus
66 |
67 | 169 | {%endblock content%} -------------------------------------------------------------------------------- /GraphSpy/templates/entra_users.html: -------------------------------------------------------------------------------- 1 | {% extends 'layout.html'%} 2 | 3 | {%block content%} 4 | 5 |
6 |
7 |

Entra ID Users

8 |
9 |
10 |
11 |
12 | 13 | 14 | 15 | 16 |
17 |
18 |
19 |
20 |
21 |
22 | 23 | 24 |
25 |
26 | 27 | 28 |
29 |
30 | 36 |
37 |
38 |
39 |
40 |
41 | 50 |
51 |
52 |
53 | 71 |
72 |
73 |
74 |

Users Table

75 | 76 |
77 |
78 | 79 | 286 | {%endblock content%} 287 | -------------------------------------------------------------------------------- /GraphSpy/templates/generic_search.html: -------------------------------------------------------------------------------- 1 | {% extends 'layout.html'%} 2 | 3 | {%block content%} 4 | 5 |
6 |
7 |

Generic MSGraph Search

8 |
9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 |
24 | 25 | 26 | Max 500 27 |
28 |
29 | 30 |
31 | 32 | 33 |
34 |
35 |
36 | 37 | 38 |
39 |
40 | 41 |
42 |
43 | 46 |
47 |
48 | 49 |
50 |

Response

51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 |
Created UserNameSummaryURL
63 |
64 | 183 | {%endblock content%} -------------------------------------------------------------------------------- /GraphSpy/templates/index.html: -------------------------------------------------------------------------------- 1 | {% extends 'layout.html'%} 2 | 3 | {%block content%} 4 |
5 |
6 | Home page 7 |
8 | Device Codes 9 |
10 | Refresh Tokens 11 |
12 | Access Tokens 13 |
14 | Graph Requests 15 |
16 | Generic Search 17 |
18 | Recent Files 19 |
20 | Files Shared With Me 21 |
22 | OneDrive 23 |
24 | SharePoint Sites 25 |
26 | SharePoint Drives 27 |
28 | SharePoint Files 29 |
30 | Outlook 31 |
32 | {%endblock content%} -------------------------------------------------------------------------------- /GraphSpy/templates/layout.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | {{title}} 46 | 47 | 48 | 49 | 148 | 152 | 153 |
154 |
155 | 156 |
157 |
158 |

Token Options

159 | 160 |
161 |
162 |
163 |
164 |
165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 |
177 |
178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 |
191 |
192 | 193 |
194 |
195 |
196 |
197 |
198 |

Access Token

199 |
200 |
201 | 202 | 203 |
204 |
205 |
206 |
207 |
208 |
User
209 |
210 |
Resource
211 |
212 |
Client ID
213 |
214 |
Scope
215 |
216 |
Expires At
217 |
218 |
219 |
220 |
221 |
222 |
223 |

Refresh Token

224 |
225 |
226 | 227 | 228 |
229 |
230 |
231 |
232 |
233 |
User
234 |
235 |
Resource
236 |
237 |
Tenant ID
238 |
239 |
Foci
240 |
241 |
Description
242 |
243 |
244 |
245 |
246 |
247 |
248 |
249 | 321 | 322 | 326 | 327 |
328 | {%block content%} 329 | {%endblock content%} 330 |
331 | 332 | 342 | 343 | -------------------------------------------------------------------------------- /GraphSpy/templates/outlook.html: -------------------------------------------------------------------------------- 1 | {% extends 'layout.html'%} 2 | 3 | {%block content%} 4 | 5 |
6 |
7 |

Outlook

8 |
9 |
10 |
11 | 12 | 13 | 14 |
15 |
16 |
17 |
18 |
19 |
20 | 21 | 22 |
23 |
24 | 25 | 26 |
27 |
28 | 40 |
41 | {%endblock content%} -------------------------------------------------------------------------------- /GraphSpy/templates/recent_files.html: -------------------------------------------------------------------------------- 1 | {% extends 'layout.html'%} 2 | 3 | {%block content%} 4 | 5 |
6 |
7 |

Recent Files

8 |
9 |
10 | 11 |
12 | 13 | 14 |
15 |
16 |
17 | 18 |
19 |
20 | 23 |
24 |
25 |
26 |

Files

27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 |
CreatedLast ModifiedFile NameFile SizeURL
40 |
41 | 145 | {%endblock content%} -------------------------------------------------------------------------------- /GraphSpy/templates/refresh_token_modal.html: -------------------------------------------------------------------------------- 1 | 2 | 39 | -------------------------------------------------------------------------------- /GraphSpy/templates/refresh_tokens.html: -------------------------------------------------------------------------------- 1 | {% extends 'layout.html'%} 2 | 3 | {%block content%} 4 | 5 |
6 |
7 |

Add Refresh Token

8 |
9 |
10 |
11 | 12 |
13 |
14 | 15 | 16 |
17 |
18 | 19 | 20 |
21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 |
33 |
34 | 35 | 36 |
37 |
38 | 39 | 40 |
41 |
42 | 43 |
44 |
45 |
46 |
47 |
48 |

Active Refresh Token

49 |
50 |
51 | 52 |
53 |
54 | 55 |
56 |
57 | 58 |
59 |
60 |
61 | 64 |
65 |
66 |

Refresh Tokens

67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 |
IDStored AtUserTenant IDResourceFociDescription
84 |
85 | 176 | {%endblock content%} -------------------------------------------------------------------------------- /GraphSpy/templates/settings.html: -------------------------------------------------------------------------------- 1 | {% extends 'layout.html'%} 2 | 3 | {%block content%} 4 | 5 |
6 |
7 |

Settings

8 |
9 |
10 |
11 | 14 | 31 |
32 |
33 | 36 | 40 |
41 |
42 |
43 |
44 | 45 | 46 |
47 |
48 |
49 |
50 |
51 |
52 |
Persistent Settings
53 |
54 |

 55 |                 
 68 |             
69 |
70 |
71 |
72 |

Databases

73 |
74 |
75 | Database Folder 76 | 77 |
78 |
79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 |
Last ModifiedSizeStateDatabase
92 |
93 |
94 |

New Database

95 |
96 |
97 | 98 | 99 |
100 |
101 | 102 |
103 |
104 |
105 |
106 | 182 | {%endblock content%} 183 | -------------------------------------------------------------------------------- /GraphSpy/templates/shared_with_me.html: -------------------------------------------------------------------------------- 1 | {% extends 'layout.html'%} 2 | 3 | {%block content%} 4 | 5 |
6 |
7 |

Files Shared with Me

8 |
9 |
10 | 11 |
12 | 13 | 14 |
15 |
16 |
17 | 18 |
19 |
20 | 23 |
24 |
25 |
26 |

Files

27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 |
CreatedLast ModifiedFile NameFile SizeURL
40 |
41 | 145 | {%endblock content%} -------------------------------------------------------------------------------- /GraphSpy/version.txt: -------------------------------------------------------------------------------- 1 | 1.5.1 -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) [year], [fullname] 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | 3. Neither the name of the copyright holder nor the names of its 16 | contributors may be used to endorse or promote products derived from 17 | this software without specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 23 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 25 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 26 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 27 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include requirements.txt 2 | recursive-include GraphSpy * -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![PyPi Version](https://img.shields.io/pypi/v/GraphSpy.svg)](https://pypi.org/project/GraphSpy/) 2 | ![Python Version](https://img.shields.io/badge/python-3.8+-blue.svg) 3 | [![GitHub Sponsors](https://img.shields.io/github/sponsors/RedByte1337?style=flat&logo=githubsponsors)](https://github.com/sponsors/RedByte1337) 4 | [![Twitter](https://img.shields.io/twitter/follow/RedByte1337?label=RedByte1337&style=social)](https://twitter.com/intent/follow?screen_name=RedByte1337) 5 | [![LinkedIn](https://img.shields.io/badge/in-Keanu_Nys-white?style=flat&logoColor=blue&labelColor=blue)](https://www.linkedin.com/in/keanunys/) 6 | 7 | # GraphSpy 8 | 9 | ``` 10 | ________ _________ 11 | / / by RedByte1337 __ / / 12 | / _____/___________ ______ | |__ / _____/_____ ______ 13 | / \ __\_ __ \__ \ \____ \| | \ \_____ \\____ \ | | 14 | \ \_\ \ | \/ __ \| |_> | \ \/ \ |_> \___ | 15 | \______ /__| |____ | __/|___| /_______ / ___/ ____| 16 | \/ \/|__| \/ \/|__| \/ 17 | ``` 18 | 19 | # Table of Contents 20 | 21 | - [GraphSpy](#graphspy) 22 | - [Table of Contents](#table-of-contents) 23 | - [Quick Start](#quick-start) 24 | - [Installation](#installation) 25 | - [Execution](#execution) 26 | - [Usage](#usage) 27 | - [Features](#features) 28 | - [Release Notes](#release-notes) 29 | - [Upcoming Features](#upcoming-features) 30 | - [Sponsors](#sponsors) 31 | - [Credits](#credits) 32 | 33 | # Quick Start 34 | 35 | ## Installation 36 | 37 | The following goes over the recommended installation process using pipx to avoid any dependency conflicts. 38 | 39 | GraphSpy is built to work on every operating system, although it was mainly tested on Linux and Windows. 40 | 41 | For other installation options and detailed instructions, check the [Installation page](https://github.com/RedByte1337/GraphSpy/wiki/Installation) on the wiki. 42 | 43 | ```bash 44 | # Install pipx (skip this if you already have it) 45 | apt install pipx 46 | pipx ensurepath 47 | 48 | # Install the latest version of GraphSpy from pypi 49 | pipx install graphspy 50 | ``` 51 | 52 | ## Execution 53 | 54 | After installation, the application can be launched using the `graphspy` command from any location on the system. 55 | 56 | Running GraphSpy without any command line arguments will launch GraphSpy and make it available at `http://127.0.0.1:5000` by default. 57 | 58 | ```bash 59 | graphspy 60 | ``` 61 | 62 | Now simply open `http://127.0.0.1:5000` in your favorite browser to get started! 63 | 64 | Use the `-i` and `-p` arguments to modify the interface and port to listen on. 65 | 66 | ```bash 67 | # Run GraphSpy on http://192.168.0.10 68 | graphspy -i 192.168.0.10 -p 80 69 | # Run GraphSpy on port 8080 on all interfaces 70 | graphspy -i 0.0.0.0 -p 8080 71 | ``` 72 | 73 | For detailed instructions and other command line arguments, please refer to the [Execution page](https://github.com/RedByte1337/GraphSpy/wiki/Execution) on the wiki. 74 | 75 | ## Usage 76 | 77 | Please refer to the [GitHub Wiki](https://github.com/RedByte1337/GraphSpy/wiki) for full usage details. 78 | 79 | For a quick feature overview, check out the [official release blog post](https://insights.spotit.be/2024/04/05/graphspy-the-swiss-army-knife-for-attacking-m365-entra/). 80 | 81 | # Features 82 | 83 | ## Access and Refresh Tokens 84 | 85 | Store your access and refresh tokens for multiple users and scopes in one location. 86 | 87 | ![Access Tokens](images/access_tokens_1.png) 88 | 89 | ![Refresh Tokens](images/refresh_tokens.png) 90 | 91 | Easily switch between them or request new access tokens from any page. 92 | 93 | ![Token Side Bar](images/token_side_bar_1.png) 94 | 95 | ## Device Codes 96 | 97 | Easily create and poll multiple device codes at once. If a user used the device code to authenticate, GraphSpy will automatically store the access and refresh token in its database. 98 | 99 | ![Device Codes](images/device_codes.png) 100 | 101 | ## MFA Methods 102 | 103 | View, modify and create MFA methods linked to the account of the user. 104 | 105 | ![MFA Methods Overview](images/mfa_methods_overview.png) 106 | 107 | The following MFA methods can be added from GraphSpy to set up persistance: 108 | - Microsoft Authenticator App 109 | - Custom OTP App, or use GraphSpy as OTP app to generate TOTP codes on the fly! 110 | - FIDO Security Keys! 111 | - Alternative email address 112 | - Mobile/Office/Alternative Phones (SMS or call) 113 | 114 | ![MFA Methods FIDO](images/mfa_methods_fido.png) 115 | 116 | ## Files and SharePoint 117 | 118 | Browse through files and folders in the user's OneDrive or any accessible SharePoint site through an intuitive file explorer interface. 119 | 120 | Of course, files can also be directly downloaded, or new files can be uploaded. 121 | 122 | ![OneDrive](images/onedrive_2.png) 123 | 124 | Additionally, list the user's recently accessed files or files shared with the user. 125 | 126 | ![Recent Files](images/recent_files.png) 127 | 128 | ## Outlook 129 | 130 | Open the user's Outlook web mail with a single click using just an Outlook access token (FOCI)! 131 | 132 | ![Outlook GraphSpy](images/outlook_1.png) 133 | 134 | ![Outlook](images/outlook_2.png) 135 | 136 | Or use the Outlook Graph module to list, read, search, delete, reply or send emails with just an access token for the MS Graph API! 137 | 138 | ![Outlook Graph Overview](images/outlook_graph_overview.png) 139 | 140 | Craft completely HTML formated emails directly in GraphSpy and include images and attachments. 141 | 142 | ![Outlook Graph Overview](images/outlook_graph_send_email.png) 143 | 144 | ## MS Teams 145 | 146 | Read and send messages using the Microsoft Teams module with a FOCI access token of the skype API (https://api.spaces.skype.com/). 147 | 148 | ![MS Teams GraphSpy](images/ms_teams.png) 149 | 150 | ## Graph Searching 151 | 152 | Search for keywords through all Microsoft 365 applications using the Microsoft Search API. 153 | 154 | For instance, use this to search for any files or emails containing keywords such as "password", "secret", ... 155 | 156 | ![Graph Search](images/graph_search_2.png) 157 | 158 | ## Custom Requests 159 | 160 | Perform custom API requests towards any endpoint using access tokens stored in GraphSpy. 161 | 162 | ![Custom Request](images/custom_requests.png) 163 | 164 | Custom request templates with variables can be stored in the database to allow easy reuse of common custom API requests. 165 | 166 | ![Custom Request](images/custom_request_templates.png) 167 | 168 | ## Entra ID 169 | 170 | List all Entra ID users and their properties using the Microsoft Graph API. 171 | 172 | ![Entra Users Overview](images/entra_users_overview.png) 173 | 174 | View additional details for a user, such as its group memberships, role assignments, devices, app roles and API permissions. 175 | 176 | ![Entra Users Details](images/entra_users_details_1.png) 177 | 178 | ## Multiple Databases 179 | 180 | GraphSpy supports multiple databases. This is useful when working on multiple assessments at once to keep your tokens and device codes organized. 181 | 182 | ![Graph Request](images/settings.png) 183 | 184 | ## Dark Mode 185 | 186 | Use the dark mode by default, or switch to light mode. 187 | 188 | # Release Notes 189 | 190 | Refer to the [Release Notes](https://github.com/RedByte1337/GraphSpy/wiki/Release-Notes) page on the GitHub Wiki 191 | 192 | # Upcoming Features 193 | 194 | * Rename files and create folders 195 | * More authentication options 196 | * Password, ESTSAuth Cookie, PRT, ... 197 | * Automatic Access Token Refreshing 198 | * Improve Microsoft Teams Module 199 | * Download authenticated files 200 | * Upload files and images 201 | * Entra ID 202 | * List Users, Groups, Applications, Devices, Conditional Access Policies, ... 203 | * Cleaner exception handling 204 | * While this should not have any direct impact on the user, edge cases might currently throw exceptions to the GraphSpy output instead of handling them in a cleaner way. 205 | 206 | # Sponsors 207 | 208 | Do you or your organization want to be featured as a key sponsor of this project here, or even mentioned within GraphSpy itself? Or do you just like GraphSpy and want to support my work? 209 | 210 | Please check out my [Sponsor page](https://github.com/sponsors/RedByte1337). 211 | 212 | _If you do not have the means to sponsor, but you still want to show your gratitude, feel free to add a star instead._ ⭐ 213 | 214 | # Credits 215 | 216 | The main motivation for creating GraphSpy was the lack of an easy to use way to perform post-compromise activities targetting Office365 applications (such as Outlook, Microsoft Teams, OneDrive, SharePoint, ...) with just an access token. 217 | 218 | While several command-line tools existed which provided some basic functionality, none of them came close to the intuitive interactive experience which the original applications provide (such as the file explorer-like interface of OneDrive and SharePoint). 219 | 220 | However, a lot of previous research was done by countless other persons (specifically regarding Device Code Phishing, which lead to the initial requirement for such a tool in the first place). 221 | 222 | * Acknowledgements 223 | * [TokenTactics](https://github.com/rvrsh3ll/TokenTactics) and [TokenTacticsV2](https://github.com/f-bader/TokenTacticsV2) 224 | * [AADInternals](https://github.com/Gerenios/AADInternals) 225 | * [Introducing a new phishing technique for compromising Office 365 accounts](https://aadinternals.com/post/phishing/) 226 | * [The Art of the Device Code Phish](https://0xboku.com/2021/07/12/ArtOfDeviceCodePhish.html) 227 | * [GraphRunner](https://github.com/dafthack/GraphRunner) is a PowerShell tool with a lot of similar features, which was released while GraphSpy was already in development. Regardless, both tools still have their distinguishing factors. 228 | * Assets 229 | * UIcons by [Flaticon](https://www.flaticon.com/uicons) -------------------------------------------------------------------------------- /images/access_tokens_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RedByte1337/GraphSpy/56912ad634e0f5379a35b79c90dd97a32e5733d2/images/access_tokens_1.png -------------------------------------------------------------------------------- /images/access_tokens_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RedByte1337/GraphSpy/56912ad634e0f5379a35b79c90dd97a32e5733d2/images/access_tokens_2.png -------------------------------------------------------------------------------- /images/custom_request_templates.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RedByte1337/GraphSpy/56912ad634e0f5379a35b79c90dd97a32e5733d2/images/custom_request_templates.png -------------------------------------------------------------------------------- /images/custom_requests.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RedByte1337/GraphSpy/56912ad634e0f5379a35b79c90dd97a32e5733d2/images/custom_requests.png -------------------------------------------------------------------------------- /images/custom_requests_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RedByte1337/GraphSpy/56912ad634e0f5379a35b79c90dd97a32e5733d2/images/custom_requests_2.png -------------------------------------------------------------------------------- /images/device_codes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RedByte1337/GraphSpy/56912ad634e0f5379a35b79c90dd97a32e5733d2/images/device_codes.png -------------------------------------------------------------------------------- /images/entra_users_custom_properties.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RedByte1337/GraphSpy/56912ad634e0f5379a35b79c90dd97a32e5733d2/images/entra_users_custom_properties.png -------------------------------------------------------------------------------- /images/entra_users_details_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RedByte1337/GraphSpy/56912ad634e0f5379a35b79c90dd97a32e5733d2/images/entra_users_details_1.png -------------------------------------------------------------------------------- /images/entra_users_details_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RedByte1337/GraphSpy/56912ad634e0f5379a35b79c90dd97a32e5733d2/images/entra_users_details_2.png -------------------------------------------------------------------------------- /images/entra_users_details_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RedByte1337/GraphSpy/56912ad634e0f5379a35b79c90dd97a32e5733d2/images/entra_users_details_3.png -------------------------------------------------------------------------------- /images/entra_users_membership_count.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RedByte1337/GraphSpy/56912ad634e0f5379a35b79c90dd97a32e5733d2/images/entra_users_membership_count.png -------------------------------------------------------------------------------- /images/entra_users_overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RedByte1337/GraphSpy/56912ad634e0f5379a35b79c90dd97a32e5733d2/images/entra_users_overview.png -------------------------------------------------------------------------------- /images/files_shared_with_me.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RedByte1337/GraphSpy/56912ad634e0f5379a35b79c90dd97a32e5733d2/images/files_shared_with_me.png -------------------------------------------------------------------------------- /images/graph_search_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RedByte1337/GraphSpy/56912ad634e0f5379a35b79c90dd97a32e5733d2/images/graph_search_1.png -------------------------------------------------------------------------------- /images/graph_search_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RedByte1337/GraphSpy/56912ad634e0f5379a35b79c90dd97a32e5733d2/images/graph_search_2.png -------------------------------------------------------------------------------- /images/mfa_methods_fido.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RedByte1337/GraphSpy/56912ad634e0f5379a35b79c90dd97a32e5733d2/images/mfa_methods_fido.png -------------------------------------------------------------------------------- /images/mfa_methods_mobile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RedByte1337/GraphSpy/56912ad634e0f5379a35b79c90dd97a32e5733d2/images/mfa_methods_mobile.png -------------------------------------------------------------------------------- /images/mfa_methods_ms_app.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RedByte1337/GraphSpy/56912ad634e0f5379a35b79c90dd97a32e5733d2/images/mfa_methods_ms_app.png -------------------------------------------------------------------------------- /images/mfa_methods_overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RedByte1337/GraphSpy/56912ad634e0f5379a35b79c90dd97a32e5733d2/images/mfa_methods_overview.png -------------------------------------------------------------------------------- /images/ms_teams.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RedByte1337/GraphSpy/56912ad634e0f5379a35b79c90dd97a32e5733d2/images/ms_teams.png -------------------------------------------------------------------------------- /images/onedrive_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RedByte1337/GraphSpy/56912ad634e0f5379a35b79c90dd97a32e5733d2/images/onedrive_1.png -------------------------------------------------------------------------------- /images/onedrive_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RedByte1337/GraphSpy/56912ad634e0f5379a35b79c90dd97a32e5733d2/images/onedrive_2.png -------------------------------------------------------------------------------- /images/outlook_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RedByte1337/GraphSpy/56912ad634e0f5379a35b79c90dd97a32e5733d2/images/outlook_1.png -------------------------------------------------------------------------------- /images/outlook_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RedByte1337/GraphSpy/56912ad634e0f5379a35b79c90dd97a32e5733d2/images/outlook_2.png -------------------------------------------------------------------------------- /images/outlook_graph_overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RedByte1337/GraphSpy/56912ad634e0f5379a35b79c90dd97a32e5733d2/images/outlook_graph_overview.png -------------------------------------------------------------------------------- /images/outlook_graph_send_email.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RedByte1337/GraphSpy/56912ad634e0f5379a35b79c90dd97a32e5733d2/images/outlook_graph_send_email.png -------------------------------------------------------------------------------- /images/recent_files.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RedByte1337/GraphSpy/56912ad634e0f5379a35b79c90dd97a32e5733d2/images/recent_files.png -------------------------------------------------------------------------------- /images/refresh_tokens.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RedByte1337/GraphSpy/56912ad634e0f5379a35b79c90dd97a32e5733d2/images/refresh_tokens.png -------------------------------------------------------------------------------- /images/settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RedByte1337/GraphSpy/56912ad634e0f5379a35b79c90dd97a32e5733d2/images/settings.png -------------------------------------------------------------------------------- /images/sharepoint_drives.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RedByte1337/GraphSpy/56912ad634e0f5379a35b79c90dd97a32e5733d2/images/sharepoint_drives.png -------------------------------------------------------------------------------- /images/sharepoint_files.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RedByte1337/GraphSpy/56912ad634e0f5379a35b79c90dd97a32e5733d2/images/sharepoint_files.png -------------------------------------------------------------------------------- /images/sharepoint_sites.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RedByte1337/GraphSpy/56912ad634e0f5379a35b79c90dd97a32e5733d2/images/sharepoint_sites.png -------------------------------------------------------------------------------- /images/token_side_bar_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RedByte1337/GraphSpy/56912ad634e0f5379a35b79c90dd97a32e5733d2/images/token_side_bar_1.png -------------------------------------------------------------------------------- /images/token_side_bar_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RedByte1337/GraphSpy/56912ad634e0f5379a35b79c90dd97a32e5733d2/images/token_side_bar_2.png -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | Flask>=3.0.0 2 | PyJWT 3 | Requests 4 | pyotp 5 | fido2 6 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | 3 | with open("README.md", 'r', encoding='utf-8') as f: 4 | readme = f.read() 5 | 6 | with open("GraphSpy/version.txt", 'r', encoding='utf-8') as f: 7 | __version__ = f.read() 8 | 9 | with open('requirements.txt', 'r', encoding='utf-8') as f: 10 | requirements = [x.strip() for x in f.readlines()] 11 | 12 | setup( 13 | name='GraphSpy', 14 | version=__version__, 15 | author='RedByte1337', 16 | url='https://github.com/RedByte1337/GraphSpy', 17 | description="Initial Access and Post-Exploitation Tool for AAD and O365 with a browser-based GUI", 18 | long_description=readme, 19 | long_description_content_type="text/markdown", 20 | install_requires=requirements, 21 | package_data={'': ['static/**/*','templates/*','version.txt']}, 22 | include_package_data=True, 23 | packages=[ 24 | "GraphSpy" 25 | ], 26 | entry_points={ 27 | "console_scripts": ["graphspy=GraphSpy.GraphSpy:main"], 28 | } 29 | ) --------------------------------------------------------------------------------