This independent academic research prototype contains two separate components:
app/: an interactive application for natural-language graph queries;ingestion/: a batch pipeline that downloads Prowler data and builds Neo4j.
Create a virtual environment and install the dependencies:
python -m venv .venv
.venv/bin/pip install -r requirements.txtCopy the environment template and configure local credentials. Never commit the
resulting .env file.
cp .env.example .envconfig.json contains non-secret endpoints and model settings. Credentials are
read from OPENAI_API_KEY, NEO4J_PASSWORD, and PROWLER_API_KEY in .env.
The development endpoints use names under the reserved .test domain:
| Service | Development hostname | Default port |
|---|---|---|
| OpenAI-compatible LLM | llm.prowler-kg.test |
1234 |
| Neo4j | neo4j.prowler-kg.test |
7687 |
| Prowler API through the IAP tunnel | localhost |
8080 |
Resolve these names through local DNS or add entries to /etc/hosts. When all
services run on the same machine, use:
127.0.0.1 llm.prowler-kg.test
127.0.0.1 neo4j.prowler-kg.test
If a service runs on another development host, replace only the corresponding
IP in the local hosts file. The Prowler IAP tunnel remains on localhost:8080
because its API validates the HTTP host header. Real infrastructure addresses
remain outside the repository.
main.py Streamlit bootstrap
app/
├── application.py application lifecycle and views
├── application_factory.py dependency construction
├── view/streamlit_view.py StreamlitView
├── view/settings_view.py read-only SettingsView
├── controller/agent_controller.py AgentController
├── text2cypher/ shared grounding, prompt and GraphRAG adapter
├── model/ configuration and Neo4j access
├── llm/ LLM interface and provider clients
└── i18n/ English and Italian UI catalogs
After Neo4j contains an imported graph, run the application from the repository root:
.venv/bin/streamlit run main.pyThe sidebar lets users switch the interface between English and Italian and
inspect the active LLM settings. Configuration is read-only in the interface:
edit config.json and restart the application to change provider, endpoint,
model, timeout, output limit, or reasoning level. Secrets remain in .env.
Before generating Cypher, the application ranks real Prowler controls and resource types from Neo4j against the user question. It adds the best candidates to the prompt using exact identifiers, reducing semantically valid-looking but incorrect queries. The application factory injects Neo4j GraphRAG as the active Text2Cypher backend.
The application is currently described by its explicit components rather than
as a strict MVC implementation: Streamlit provides presentation,
AgentController coordinates requests, and the Text2Cypher package contains
grounding, prompt and GraphRAG integration.
The main runtime dependencies and question-processing flow are summarized in
the application architecture.
ingestion/
├── download_prowler_data.py Prowler API → JSON
├── import_cis_graph.py JSON → Neo4j
├── scanner/ Prowler API client
├── services/cis_graph_builder.py graph transformation
└── database/ Neo4j write repository
Operational commands are documented in ingestion/README.md.
The current Neo4j knowledge model is documented in
docs/knowledge-graph-model.md, including its
nodes, relationships, transformation flow, and an example query.
Application interactions and the planned Human-in-the-Loop remediation flow
are summarized in the
use-case diagram.
The Windows, WSL2 Ubuntu, Visual Studio Code, Codex, and runtime setup is
described in the
development environment documentation.
When enabled in config.json, every Text-to-Cypher run is recorded as
correlated, Pydantic-validated JSON Lines events in
evidence/raw/llm_runs.jsonl. Schema version 1.0 defines separate fixed
contracts for llm_request, completed, and failed; missing required fields,
unexpected fields, and invalid types are rejected before persistence. Records include
the full prompt, live schema, semantic context, configured model, locale, raw and
clean Cypher, timing, result count, and failure stage. Raw evidence is ignored by
Git because it may contain infrastructure metadata.
Completed events can also include bounded Neo4j result records and identify the active Text2Cypher backend.
Reasoning evidence records the provider-compatible configured value
(reasoning_setting) and any reasoning_tokens reported in the completion
response. The configured value is passed through unchanged: OpenAI-compatible
clients send it as reasoning_effort, while Ollama sends it as think.
Provider usage is normalized into the fixed keys prompt_tokens,
completion_tokens, reasoning_tokens, and total_tokens; an unavailable
metric is recorded as JSON null, never omitted.
.venv/bin/python -m unittest discover -s tests -vThis project is an independent academic research prototype. It is not affiliated with, endorsed by, sponsored by, or approved by the Center for Internet Security, Inc. or Prowler. CIS Benchmarks® is a registered trademark of the Center for Internet Security, Inc. Prowler is referenced solely as a supported data source.
The repository does not distribute the CIS Benchmark. Benchmark content used at runtime must be obtained from an authorized source. Third-party names, trademarks, and source data remain subject to their respective owners' terms.
For questions, research collaboration, or professional opportunities: sirag.gabag@gmail.com
The original source code and documentation in this project are released under
the permissive BSD Zero Clause License (0BSD). This license does
not grant rights to third-party trademarks or source data.