entrascope 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- entrascope-0.1.0/.gitignore +218 -0
- entrascope-0.1.0/LICENSE +21 -0
- entrascope-0.1.0/PKG-INFO +271 -0
- entrascope-0.1.0/README.md +231 -0
- entrascope-0.1.0/config/capabilities.yaml +133 -0
- entrascope-0.1.0/config/credentials.yaml +34 -0
- entrascope-0.1.0/config/endpoints.yaml +53 -0
- entrascope-0.1.0/config/error-codes.yaml +164 -0
- entrascope-0.1.0/config/fields.yaml +157 -0
- entrascope-0.1.0/config/kql/audit_applicationmanagement.kql +22 -0
- entrascope-0.1.0/config/kql/graph_activity.kql +22 -0
- entrascope-0.1.0/config/kql/signins_failures.kql +22 -0
- entrascope-0.1.0/config/logging.yaml +59 -0
- entrascope-0.1.0/config/retry.yaml +61 -0
- entrascope-0.1.0/config/server.yaml +56 -0
- entrascope-0.1.0/config/tables.yaml +85 -0
- entrascope-0.1.0/pyproject.toml +98 -0
- entrascope-0.1.0/src/entrascope/__init__.py +5 -0
- entrascope-0.1.0/src/entrascope/__main__.py +6 -0
- entrascope-0.1.0/src/entrascope/capabilities.py +251 -0
- entrascope-0.1.0/src/entrascope/cli.py +915 -0
- entrascope-0.1.0/src/entrascope/config.py +510 -0
- entrascope-0.1.0/src/entrascope/credentials.py +378 -0
- entrascope-0.1.0/src/entrascope/discovery.py +458 -0
- entrascope-0.1.0/src/entrascope/doctor.py +337 -0
- entrascope-0.1.0/src/entrascope/errors.py +104 -0
- entrascope-0.1.0/src/entrascope/graph.py +262 -0
- entrascope-0.1.0/src/entrascope/http.py +360 -0
- entrascope-0.1.0/src/entrascope/investigate.py +447 -0
- entrascope-0.1.0/src/entrascope/logger.py +227 -0
- entrascope-0.1.0/src/entrascope/logs.py +371 -0
- entrascope-0.1.0/src/entrascope/mcp_http.py +305 -0
- entrascope-0.1.0/src/entrascope/mcp_stdio.py +61 -0
- entrascope-0.1.0/src/entrascope/mcp_tools.py +366 -0
- entrascope-0.1.0/src/entrascope/models.py +355 -0
- entrascope-0.1.0/src/entrascope/monitor.py +136 -0
- entrascope-0.1.0/src/entrascope/redaction.py +94 -0
- entrascope-0.1.0/src/entrascope/render.py +179 -0
- entrascope-0.1.0/tests/__init__.py +1 -0
- entrascope-0.1.0/tests/conftest.py +79 -0
- entrascope-0.1.0/tests/fixtures/.gitkeep +0 -0
- entrascope-0.1.0/tests/fixtures/applications.json +105 -0
- entrascope-0.1.0/tests/fixtures/audit_events.json +44 -0
- entrascope-0.1.0/tests/fixtures/federated_credentials.json +11 -0
- entrascope-0.1.0/tests/fixtures/owners.json +9 -0
- entrascope-0.1.0/tests/fixtures/permission_grants.json +20 -0
- entrascope-0.1.0/tests/fixtures/service_principals.json +91 -0
- entrascope-0.1.0/tests/fixtures/sign_ins.json +34 -0
- entrascope-0.1.0/tests/test_cli.py +524 -0
- entrascope-0.1.0/tests/test_config.py +226 -0
- entrascope-0.1.0/tests/test_credentials.py +259 -0
- entrascope-0.1.0/tests/test_discovery.py +359 -0
- entrascope-0.1.0/tests/test_doctor.py +339 -0
- entrascope-0.1.0/tests/test_errors.py +122 -0
- entrascope-0.1.0/tests/test_graph.py +258 -0
- entrascope-0.1.0/tests/test_guards.py +221 -0
- entrascope-0.1.0/tests/test_http.py +347 -0
- entrascope-0.1.0/tests/test_investigate.py +386 -0
- entrascope-0.1.0/tests/test_logger.py +289 -0
- entrascope-0.1.0/tests/test_logs.py +278 -0
- entrascope-0.1.0/tests/test_mcp_http_auth.py +433 -0
- entrascope-0.1.0/tests/test_mcp_tools.py +268 -0
- entrascope-0.1.0/tests/test_monitor.py +146 -0
- entrascope-0.1.0/tests/test_render.py +152 -0
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[codz]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
parts/
|
|
20
|
+
sdist/
|
|
21
|
+
var/
|
|
22
|
+
wheels/
|
|
23
|
+
share/python-wheels/
|
|
24
|
+
*.egg-info/
|
|
25
|
+
.installed.cfg
|
|
26
|
+
*.egg
|
|
27
|
+
MANIFEST
|
|
28
|
+
|
|
29
|
+
# PyInstaller
|
|
30
|
+
# Usually these files are written by a python script from a template
|
|
31
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
32
|
+
*.manifest
|
|
33
|
+
*.spec
|
|
34
|
+
|
|
35
|
+
# Installer logs
|
|
36
|
+
pip-log.txt
|
|
37
|
+
pip-delete-this-directory.txt
|
|
38
|
+
|
|
39
|
+
# Unit test / coverage reports
|
|
40
|
+
htmlcov/
|
|
41
|
+
.tox/
|
|
42
|
+
.nox/
|
|
43
|
+
.coverage
|
|
44
|
+
.coverage.*
|
|
45
|
+
.cache
|
|
46
|
+
nosetests.xml
|
|
47
|
+
coverage.xml
|
|
48
|
+
*.cover
|
|
49
|
+
*.py.cover
|
|
50
|
+
.hypothesis/
|
|
51
|
+
.pytest_cache/
|
|
52
|
+
cover/
|
|
53
|
+
|
|
54
|
+
# Translations
|
|
55
|
+
*.mo
|
|
56
|
+
*.pot
|
|
57
|
+
|
|
58
|
+
# Django stuff:
|
|
59
|
+
*.log
|
|
60
|
+
local_settings.py
|
|
61
|
+
db.sqlite3
|
|
62
|
+
db.sqlite3-journal
|
|
63
|
+
|
|
64
|
+
# Flask stuff:
|
|
65
|
+
instance/
|
|
66
|
+
.webassets-cache
|
|
67
|
+
|
|
68
|
+
# Scrapy stuff:
|
|
69
|
+
.scrapy
|
|
70
|
+
|
|
71
|
+
# Sphinx documentation
|
|
72
|
+
docs/_build/
|
|
73
|
+
|
|
74
|
+
# PyBuilder
|
|
75
|
+
.pybuilder/
|
|
76
|
+
target/
|
|
77
|
+
|
|
78
|
+
# Jupyter Notebook
|
|
79
|
+
.ipynb_checkpoints
|
|
80
|
+
|
|
81
|
+
# IPython
|
|
82
|
+
profile_default/
|
|
83
|
+
ipython_config.py
|
|
84
|
+
|
|
85
|
+
# pyenv
|
|
86
|
+
# For a library or package, you might want to ignore these files since the code is
|
|
87
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
88
|
+
# .python-version
|
|
89
|
+
|
|
90
|
+
# pipenv
|
|
91
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
92
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
93
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
94
|
+
# install all needed dependencies.
|
|
95
|
+
# Pipfile.lock
|
|
96
|
+
|
|
97
|
+
# UV
|
|
98
|
+
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
|
|
99
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
100
|
+
# commonly ignored for libraries.
|
|
101
|
+
# uv.lock
|
|
102
|
+
|
|
103
|
+
# poetry
|
|
104
|
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
|
105
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
106
|
+
# commonly ignored for libraries.
|
|
107
|
+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
|
108
|
+
# poetry.lock
|
|
109
|
+
# poetry.toml
|
|
110
|
+
|
|
111
|
+
# pdm
|
|
112
|
+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
|
113
|
+
# pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
|
|
114
|
+
# https://pdm-project.org/en/latest/usage/project/#working-with-version-control
|
|
115
|
+
# pdm.lock
|
|
116
|
+
# pdm.toml
|
|
117
|
+
.pdm-python
|
|
118
|
+
.pdm-build/
|
|
119
|
+
|
|
120
|
+
# pixi
|
|
121
|
+
# Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
|
|
122
|
+
# pixi.lock
|
|
123
|
+
# Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
|
|
124
|
+
# in the .venv directory. It is recommended not to include this directory in version control.
|
|
125
|
+
.pixi
|
|
126
|
+
|
|
127
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
|
128
|
+
__pypackages__/
|
|
129
|
+
|
|
130
|
+
# Celery stuff
|
|
131
|
+
celerybeat-schedule
|
|
132
|
+
celerybeat.pid
|
|
133
|
+
|
|
134
|
+
# Redis
|
|
135
|
+
*.rdb
|
|
136
|
+
*.aof
|
|
137
|
+
*.pid
|
|
138
|
+
|
|
139
|
+
# RabbitMQ
|
|
140
|
+
mnesia/
|
|
141
|
+
rabbitmq/
|
|
142
|
+
rabbitmq-data/
|
|
143
|
+
|
|
144
|
+
# ActiveMQ
|
|
145
|
+
activemq-data/
|
|
146
|
+
|
|
147
|
+
# SageMath parsed files
|
|
148
|
+
*.sage.py
|
|
149
|
+
|
|
150
|
+
# Environments
|
|
151
|
+
.env
|
|
152
|
+
.envrc
|
|
153
|
+
.venv
|
|
154
|
+
env/
|
|
155
|
+
venv/
|
|
156
|
+
ENV/
|
|
157
|
+
env.bak/
|
|
158
|
+
venv.bak/
|
|
159
|
+
|
|
160
|
+
# Spyder project settings
|
|
161
|
+
.spyderproject
|
|
162
|
+
.spyproject
|
|
163
|
+
|
|
164
|
+
# Rope project settings
|
|
165
|
+
.ropeproject
|
|
166
|
+
|
|
167
|
+
# mkdocs documentation
|
|
168
|
+
/site
|
|
169
|
+
|
|
170
|
+
# mypy
|
|
171
|
+
.mypy_cache/
|
|
172
|
+
.dmypy.json
|
|
173
|
+
dmypy.json
|
|
174
|
+
|
|
175
|
+
# Pyre type checker
|
|
176
|
+
.pyre/
|
|
177
|
+
|
|
178
|
+
# pytype static type analyzer
|
|
179
|
+
.pytype/
|
|
180
|
+
|
|
181
|
+
# Cython debug symbols
|
|
182
|
+
cython_debug/
|
|
183
|
+
|
|
184
|
+
# PyCharm
|
|
185
|
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
|
186
|
+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
|
187
|
+
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
|
188
|
+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
|
189
|
+
# .idea/
|
|
190
|
+
|
|
191
|
+
# Abstra
|
|
192
|
+
# Abstra is an AI-powered process automation framework.
|
|
193
|
+
# Ignore directories containing user credentials, local state, and settings.
|
|
194
|
+
# Learn more at https://abstra.io/docs
|
|
195
|
+
.abstra/
|
|
196
|
+
|
|
197
|
+
# Visual Studio Code
|
|
198
|
+
# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
|
|
199
|
+
# that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
|
|
200
|
+
# and can be added to the global gitignore or merged into this file. However, if you prefer,
|
|
201
|
+
# you could uncomment the following to ignore the entire vscode folder
|
|
202
|
+
# .vscode/
|
|
203
|
+
# Temporary file for partial code execution
|
|
204
|
+
tempCodeRunnerFile.py
|
|
205
|
+
|
|
206
|
+
# Ruff stuff:
|
|
207
|
+
.ruff_cache/
|
|
208
|
+
|
|
209
|
+
# PyPI configuration file
|
|
210
|
+
.pypirc
|
|
211
|
+
|
|
212
|
+
# Marimo
|
|
213
|
+
marimo/_static/
|
|
214
|
+
marimo/_lsp/
|
|
215
|
+
__marimo__/
|
|
216
|
+
|
|
217
|
+
# Streamlit
|
|
218
|
+
.streamlit/secrets.toml
|
entrascope-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Dejan Gregor
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: entrascope
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Observability over Microsoft Entra ID and Azure Monitor logs for diagnosing application authentication and authorisation failures.
|
|
5
|
+
Project-URL: Homepage, https://github.com/SCGIS-Wales/entrascope
|
|
6
|
+
Project-URL: Repository, https://github.com/SCGIS-Wales/entrascope
|
|
7
|
+
Project-URL: Issues, https://github.com/SCGIS-Wales/entrascope/issues
|
|
8
|
+
Author: Dejan Gregor
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: azure,entra,mcp,oauth,observability
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Environment :: Console
|
|
14
|
+
Classifier: Intended Audience :: System Administrators
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
16
|
+
Classifier: Topic :: Security
|
|
17
|
+
Classifier: Topic :: System :: Systems Administration
|
|
18
|
+
Requires-Python: >=3.14
|
|
19
|
+
Requires-Dist: azure-identity>=1.25.3
|
|
20
|
+
Requires-Dist: azure-monitor-query>=2.0.0
|
|
21
|
+
Requires-Dist: click>=8.4.0
|
|
22
|
+
Requires-Dist: fastmcp>=3.4.7
|
|
23
|
+
Requires-Dist: pydantic>=2.13.0
|
|
24
|
+
Requires-Dist: pyjwt[crypto]>=2.13.0
|
|
25
|
+
Requires-Dist: pyyaml>=6.0.2
|
|
26
|
+
Requires-Dist: requests>=2.32.0
|
|
27
|
+
Requires-Dist: rich>=14.0.0
|
|
28
|
+
Requires-Dist: urllib3>=2.0.0
|
|
29
|
+
Provides-Extra: dev
|
|
30
|
+
Requires-Dist: mypy>=1.14.0; extra == 'dev'
|
|
31
|
+
Requires-Dist: pytest-asyncio>=0.24.0; extra == 'dev'
|
|
32
|
+
Requires-Dist: pytest-cov>=6.0.0; extra == 'dev'
|
|
33
|
+
Requires-Dist: pytest-timeout>=2.3.0; extra == 'dev'
|
|
34
|
+
Requires-Dist: pytest>=8.3.0; extra == 'dev'
|
|
35
|
+
Requires-Dist: responses>=0.26.0; extra == 'dev'
|
|
36
|
+
Requires-Dist: ruff>=0.9.0; extra == 'dev'
|
|
37
|
+
Requires-Dist: types-pyyaml>=6.0.12; extra == 'dev'
|
|
38
|
+
Requires-Dist: types-requests>=2.32.0; extra == 'dev'
|
|
39
|
+
Description-Content-Type: text/markdown
|
|
40
|
+
|
|
41
|
+
# entrascope
|
|
42
|
+
|
|
43
|
+
Observability and diagnostics for Microsoft Entra ID and Azure Monitor, helping
|
|
44
|
+
engineers troubleshoot authentication and authorisation failures.
|
|
45
|
+
|
|
46
|
+
> Entra directory operations do not appear in the Azure subscription activity
|
|
47
|
+
> log. They are recorded in the Entra audit logs, under the category
|
|
48
|
+
> ApplicationManagement. entrascope reads them through Microsoft Graph and
|
|
49
|
+
> through Azure Monitor.
|
|
50
|
+
|
|
51
|
+
[](https://github.com/SCGIS-Wales/entrascope/actions/workflows/ci.yml)
|
|
52
|
+
[](https://www.python.org/downloads/)
|
|
53
|
+
[](LICENSE)
|
|
54
|
+
|
|
55
|
+
## What it does
|
|
56
|
+
|
|
57
|
+
- **Discovery.** Enumerate application registrations and enterprise
|
|
58
|
+
applications of every type, and project sign in audience, redirect URIs,
|
|
59
|
+
requested and granted permissions, owners, credentials and their expiry,
|
|
60
|
+
federated identity credentials, SAML configuration and the assignment
|
|
61
|
+
requirement.
|
|
62
|
+
- **Log interrogation.** Read Entra audit logs, interactive and non interactive
|
|
63
|
+
user sign ins, service principal and managed identity sign ins, Microsoft
|
|
64
|
+
Graph activity and provisioning logs.
|
|
65
|
+
- **Capability detection.** Report when the logging you need is not enabled,
|
|
66
|
+
which licence tier it requires and how to switch it on.
|
|
67
|
+
- **Error explanation.** Map AADSTS and Microsoft Graph error codes to meaning,
|
|
68
|
+
likely cause and remediation.
|
|
69
|
+
|
|
70
|
+
## Three surfaces, one core
|
|
71
|
+
|
|
72
|
+
| Surface | Transport | Authentication |
|
|
73
|
+
| --- | --- | --- |
|
|
74
|
+
| Command line | local | credential file, environment, Azure CLI or DefaultAzureCredential |
|
|
75
|
+
| Local MCP server | stdio | the same, no OAuth |
|
|
76
|
+
| Remote MCP server | Streamable HTTP | OAuth 2.1 resource server validating Entra tokens |
|
|
77
|
+
|
|
78
|
+
## Installation
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
pip install entrascope
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
From a clone, for development:
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
python3.14 -m venv .venv && .venv/bin/pip install -e ".[dev]"
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Authentication
|
|
91
|
+
|
|
92
|
+
The quickest route needs nothing but an Azure CLI session:
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
az login
|
|
96
|
+
entrascope doctor --auth azure-cli
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
For unattended use, place client credentials at
|
|
100
|
+
`~/.entra/provisioner-credentials.json` with the keys `ClientID`, `Secret` and
|
|
101
|
+
`TenantID`. The file must be mode 0600 inside a directory of mode 0700, and
|
|
102
|
+
entrascope refuses to run otherwise.
|
|
103
|
+
|
|
104
|
+
## Using it
|
|
105
|
+
|
|
106
|
+
Run `entrascope` with no arguments and it tells you what it can do. Every group
|
|
107
|
+
and every command carries its own help and worked examples, so `--help` is
|
|
108
|
+
always the next step.
|
|
109
|
+
|
|
110
|
+
### Terminology, used the same way throughout
|
|
111
|
+
|
|
112
|
+
| Term | Meaning |
|
|
113
|
+
| --- | --- |
|
|
114
|
+
| application registration | what you register in Entra, the definition |
|
|
115
|
+
| enterprise application | the service principal, the instance in a tenant |
|
|
116
|
+
| delegated permission | acts as a signed in person, a `scp` claim |
|
|
117
|
+
| application permission | acts as itself, a `roles` claim |
|
|
118
|
+
|
|
119
|
+
### Diagnosing a failure
|
|
120
|
+
|
|
121
|
+
Start wide, then narrow. `investigate` gathers credentials, directory changes
|
|
122
|
+
and sign in failures, applies a set of rules and ranks what it finds worst
|
|
123
|
+
first, with the remediation for each.
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
entrascope doctor # can entrascope see what it needs
|
|
127
|
+
entrascope investigate # what is wrong in this tenant
|
|
128
|
+
entrascope investigate --severity error # only what is already broken
|
|
129
|
+
entrascope investigate my-api # narrow to one application
|
|
130
|
+
entrascope investigate my-api --full # and show the evidence behind it
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
The argument to `investigate` is an application id, an object id or part of a
|
|
134
|
+
display name, whichever the error message gave you. The same value works as
|
|
135
|
+
`--app` on every other command. Findings are ranked **error** for something
|
|
136
|
+
already broken, **warning** for something that will break, and **note** for the
|
|
137
|
+
context that explains a result.
|
|
138
|
+
|
|
139
|
+
### Looking at one thing at a time
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
entrascope discover applications --expiring # credentials about to expire
|
|
143
|
+
entrascope discover applications --type single-page-application
|
|
144
|
+
entrascope discover enterprise-apps --type managed-identity
|
|
145
|
+
entrascope discover applications --app my-api --output json
|
|
146
|
+
|
|
147
|
+
entrascope logs audit --failures-only # failed directory changes
|
|
148
|
+
entrascope logs audit --app my-api
|
|
149
|
+
entrascope logs signins --kind service-principal --failures-only
|
|
150
|
+
entrascope logs signins --app my-api --hours 6
|
|
151
|
+
entrascope logs graph-activity --workspace <workspace-id>
|
|
152
|
+
entrascope logs kinds # which sign in kinds exist
|
|
153
|
+
|
|
154
|
+
entrascope errors explain AADSTS7000215
|
|
155
|
+
entrascope errors explain "AADSTS50011: The redirect URI does not match"
|
|
156
|
+
entrascope errors search consent
|
|
157
|
+
entrascope errors list
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
`discover apps` and `discover sps` still work as short forms.
|
|
161
|
+
|
|
162
|
+
### Reading the same data two ways
|
|
163
|
+
|
|
164
|
+
Audit events and sign ins can be answered by Microsoft Graph or by Azure
|
|
165
|
+
Monitor, and both return the same fields.
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
entrascope logs audit --route graph # any tenant
|
|
169
|
+
entrascope logs audit --route monitor --workspace <id> # longer retention
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
The Graph route needs only the right permission. The Monitor route needs a
|
|
173
|
+
diagnostic setting and the Log Analytics Reader role, and gives longer
|
|
174
|
+
retention. Microsoft Graph activity exists only through Azure Monitor. Sign in
|
|
175
|
+
logs of any kind need an Entra ID P1 or P2 licence; audit logs do not.
|
|
176
|
+
|
|
177
|
+
### Output and identity
|
|
178
|
+
|
|
179
|
+
`--output json` and `--output yaml` work on every command and are quiet, so the
|
|
180
|
+
output can be piped straight into another tool. `--auth` chooses the identity:
|
|
181
|
+
`file`, `env`, `azure-cli` or `default`. `errors explain`, `errors list` and
|
|
182
|
+
`errors search` need no credentials at all, because the mapping is
|
|
183
|
+
configuration.
|
|
184
|
+
|
|
185
|
+
## As an MCP server
|
|
186
|
+
|
|
187
|
+
```bash
|
|
188
|
+
entrascope serve stdio
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
Register it with an assistant that speaks the Model Context Protocol. stdio has
|
|
192
|
+
no OAuth, so credentials come from the environment or the credential file
|
|
193
|
+
exactly as they do for every other command, and the server runs with your
|
|
194
|
+
privileges. Every tool reads. None of them changes the directory.
|
|
195
|
+
|
|
196
|
+
The tool surface mirrors the commands: `doctor`, `discover_applications`,
|
|
197
|
+
`discover_service_principals`, `audit_events`, `sign_ins`, `graph_activity`,
|
|
198
|
+
`explain_error`, `list_error_codes` and `sign_in_kinds`. A tool result and the
|
|
199
|
+
corresponding `--output json` payload are the same bytes, which a test
|
|
200
|
+
enforces.
|
|
201
|
+
|
|
202
|
+
### As a remote server
|
|
203
|
+
|
|
204
|
+
```bash
|
|
205
|
+
entrascope serve http --host 0.0.0.0 --port 8000
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
An OAuth 2.1 protected resource validating Entra issued bearer tokens.
|
|
209
|
+
Terminate TLS at a reverse proxy and set `ENTRASCOPE_BASE_URL` to the canonical
|
|
210
|
+
https URI, which appears in the protected resource metadata and which clients
|
|
211
|
+
bind their tokens to. Set `ENTRASCOPE_TENANT_ID` and `ENTRASCOPE_CLIENT_ID` for
|
|
212
|
+
the application registration this server presents.
|
|
213
|
+
|
|
214
|
+
The audience must equal the application id URI, a token issued for anything
|
|
215
|
+
else is refused, and the caller's token is never forwarded to Microsoft Graph:
|
|
216
|
+
Graph is called with the server's own credentials, because the data is tenant
|
|
217
|
+
scoped rather than caller scoped.
|
|
218
|
+
|
|
219
|
+
A container image is built from the `Dockerfile`, running as a non root user on
|
|
220
|
+
`python:3.14-slim`.
|
|
221
|
+
|
|
222
|
+
## Corporate networks
|
|
223
|
+
|
|
224
|
+
entrascope honours a forward web proxy from `HTTPS_PROXY`, `HTTP_PROXY`,
|
|
225
|
+
`ALL_PROXY` and `NO_PROXY`, and verifies TLS against a private certificate
|
|
226
|
+
authority named in `ENTRASCOPE_CA_BUNDLE`, `REQUESTS_CA_BUNDLE`,
|
|
227
|
+
`SSL_CERT_FILE`, `CURL_CA_BUNDLE` or the `SSL_CERT_DIR` directory. The same
|
|
228
|
+
trust reaches the token endpoint and Azure Monitor. Run `entrascope doctor` to
|
|
229
|
+
see exactly which proxy and which certificate authority are in force.
|
|
230
|
+
|
|
231
|
+
## Documentation
|
|
232
|
+
|
|
233
|
+
Steering documents live in [docs/steering](docs/steering):
|
|
234
|
+
[product](docs/steering/product.md),
|
|
235
|
+
[technology stack](docs/steering/tech-stack.md),
|
|
236
|
+
[repository structure](docs/steering/repo-structure.md),
|
|
237
|
+
[coding standards](docs/steering/coding-standards.md),
|
|
238
|
+
[configuration](docs/steering/configuration.md),
|
|
239
|
+
[credentials and security](docs/steering/credentials-and-security.md),
|
|
240
|
+
[Graph and Monitor](docs/steering/graph-and-monitor.md),
|
|
241
|
+
[MCP server](docs/steering/mcp-server.md),
|
|
242
|
+
[testing strategy](docs/steering/testing-strategy.md),
|
|
243
|
+
[release and publishing](docs/steering/release-and-publishing.md) and the
|
|
244
|
+
[phased task plan](docs/steering/tasks.md).
|
|
245
|
+
|
|
246
|
+
## Contributing
|
|
247
|
+
|
|
248
|
+
One change is one pull request, and every check must pass before it merges. The
|
|
249
|
+
gate is `ruff check`, `ruff format --check`, `mypy --strict src` and `pytest`,
|
|
250
|
+
plus five structural guards: no endpoint or table name written into code, no
|
|
251
|
+
class without a framework contract comment, no secret in any output, one HTTP
|
|
252
|
+
stack, one logger.
|
|
253
|
+
|
|
254
|
+
```bash
|
|
255
|
+
python3.14 -m venv .venv
|
|
256
|
+
.venv/bin/pip install -e ".[dev]"
|
|
257
|
+
.venv/bin/ruff check src/ tests/ && .venv/bin/mypy --strict src && .venv/bin/pytest
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
The rules the code follows, and why, are in
|
|
261
|
+
[docs/steering](docs/steering). Read
|
|
262
|
+
[coding-standards.md](docs/steering/coding-standards.md) first.
|
|
263
|
+
|
|
264
|
+
## Security
|
|
265
|
+
|
|
266
|
+
Reporting a vulnerability, what entrascope does with credentials, and the rules
|
|
267
|
+
the remote server holds to: [SECURITY.md](SECURITY.md).
|
|
268
|
+
|
|
269
|
+
## Licence
|
|
270
|
+
|
|
271
|
+
MIT. See [LICENSE](LICENSE).
|