Skip to content
Merged

Dev #61

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
de371f4
chore: add skeleton files and requirements
Vishnu2707 Apr 25, 2026
dd24ce0
fix: remove embedded git repo
Vishnu2707 Apr 25, 2026
e872074
Core Structure Created
Vishnu2707 Apr 25, 2026
ee77377
feat: build complete core — scanner engine, 10 rules, API, playbooks,…
Vishnu2707 Apr 25, 2026
053be03
docs: replace ASCII architecture with interactive Mermaid diagram
Vishnu2707 Apr 25, 2026
b31ecb7
feat: Sentinel integration — ingest.py, 4 KQL rules, setup guide (#12)
TFT444 May 2, 2026
d545744
fix: add AZ-STOR-003 compliance mappings, correct NIST control to PR.…
Vishnu2707 May 4, 2026
6c0c58e
docs: add real-world breach scenarios for all 10 starter rules (#15)
TFT444 May 4, 2026
e4382cd
feat: add AZ-KV-002 key vault public access rule and remediation play…
parthrohit22 May 4, 2026
7593ba0
Merge branch 'main' into dev
Vishnu2707 May 4, 2026
0ec2290
Merge remote-tracking branch 'origin/main' into dev
Vishnu2707 May 4, 2026
e8fed83
docs: update README with rule count, roadmap progress and contributors
Vishnu2707 May 4, 2026
35312d4
feat: add network security rules AZ-NET-003 to AZ-NET-010 (#16)
TFT444 May 4, 2026
aee88b2
Merge remote-tracking branch 'origin/main' into dev
Vishnu2707 May 4, 2026
2badbce
Feat/az stor 003 (#21)
ritiksah141 May 5, 2026
1e7a81f
docs: add SOC 2 Type II compliance framework mapping (#33)
TFT444 May 8, 2026
f409b67
Refactor/azure client network methods (#22)
TFT444 May 9, 2026
bb47779
feat: add CI pipeline with 6 automated checks (#34)
ritiksah141 May 9, 2026
0d99e2d
Merge branch 'main' into dev
Vishnu2707 May 9, 2026
46096a6
Merge remote-tracking branch 'origin/main' into dev
Vishnu2707 May 9, 2026
9e5d355
docs: update .github/ISSUE_TEMPLATE/new_rule.md to reflect current co…
Vishnu2707 May 9, 2026
2a5655e
docs: update .github/PULL_REQUEST_TEMPLATE.md to reflect current code…
Vishnu2707 May 9, 2026
57f25a6
docs: update CONTRIBUTING.md to reflect current codebase state
Vishnu2707 May 9, 2026
309deca
docs: update README.md to reflect current codebase state
Vishnu2707 May 9, 2026
693b20c
docs: update compliance/frameworks/iso27001.json to reflect current c…
Vishnu2707 May 9, 2026
c292efc
docs: update compliance/frameworks/nist_csf.json to reflect current c…
Vishnu2707 May 9, 2026
034b9d5
docs: update docs/adding-a-rule.md to reflect current codebase state
Vishnu2707 May 9, 2026
936a7d6
docs: update docs/architecture.md to reflect current codebase state
Vishnu2707 May 9, 2026
3cd0f00
docs: update docs/az-stor-003-test-plan.md to reflect current codebas…
Vishnu2707 May 9, 2026
17c29f4
docs: update docs/azure-setup.md to reflect current codebase state
Vishnu2707 May 9, 2026
6275396
docs: update docs/ci-pipeline.md to reflect current codebase state
Vishnu2707 May 9, 2026
ab16a16
docs: update docs/sentinel-setup.md to reflect current codebase state
Vishnu2707 May 9, 2026
1cd89dd
docs: update sentinel/TEST_PLAN.md to reflect current codebase state
Vishnu2707 May 9, 2026
a2fed2e
docs: update docs/api-reference.md to reflect current codebase state
Vishnu2707 May 9, 2026
98894bc
docs: update docs/rules-reference.md to reflect current codebase state
Vishnu2707 May 9, 2026
fdae7e7
Merge remote-tracking branch 'origin/dev' into dev
Vishnu2707 May 9, 2026
85bbb7f
docs: update README.md for professional open source style
Vishnu2707 May 9, 2026
0643eaf
docs: update CONTRIBUTING.md for professional open source style
Vishnu2707 May 9, 2026
5ebcdd9
docs: update docs/adding-a-rule.md for professional open source style
Vishnu2707 May 9, 2026
eb88659
Merge branch 'main' into dev
Vishnu2707 May 9, 2026
2d230dd
docs: update deployment guide to use Render instead of Azure App Service
Vishnu2707 May 9, 2026
bac6146
Merge remote-tracking branch 'origin/dev' into dev
Vishnu2707 May 9, 2026
d4384fe
feat: add rule AZ-STOR-004 storage account diagnostic logging check (…
SHAURYAKSHARMA24 May 13, 2026
826396a
feat: add rule AZ-IDN-003 Adds scanner rule AZ-IDN-003 detecting Entr…
TFT444 May 13, 2026
cd47b68
feat: add rule AZ-CMP-002 — VM disk not protected by CMK or ADE (#47)
TFT444 May 13, 2026
1efe1f3
Feat/api deployment (#46)
ritiksah141 May 13, 2026
ba6c70c
feat: AZ-NET-011 Network Watcher not enabled in all regions (#42)
emon22-ts May 13, 2026
e7c3487
feat: add AZ-DB-003 PostgreSQL Flexible Server SSL enforcement rule a…
emon22-ts May 16, 2026
024e635
Merge branch 'main' into dev
Vishnu2707 May 16, 2026
bc146ef
[RULE] AZ-CMP-003: VM without endpoint protection installed (#57)
TFT444 May 23, 2026
923cc75
[DOCS] Add OpenShield learning and onboarding portal (#51)
parthrohit22 May 23, 2026
954505c
Merge branch 'main' into dev
Vishnu2707 May 24, 2026
4a2ef01
refactor: reuse database connection per request using Flask g (#41)
safidnadaf May 24, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ openshield/

---


## Quick Start

```bash
Expand Down Expand Up @@ -185,4 +186,20 @@ MIT — free to use, modify, and distribute.

---

> Built with ❤️ by security engineers and students who believe cloud security tooling should be accessible to everyone.

---

## Learn OpenShield

Explore the OpenShield learning portal to understand:

- Azure CSPM fundamentals
- OpenShield architecture
- Compliance mappings
- Remediation workflows
- Contributor onboarding
- Documentation navigation

👉 [OpenShield Learn](docs/learn/index.html)
> Built by security engineers and students who believe cloud security tooling should be accessible to everyone.
6 changes: 3 additions & 3 deletions api/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,9 @@ def create_app() -> Flask:
# ------------------------------------------------------------------ #

@app.teardown_appcontext
def close_db(error):
def close_db(error=None):
"""Ensure the database connection is closed after the request."""
db = g.pop("db_conn", None)
db = g.pop("db", None)
if db is not None:
try:
if hasattr(db, "conn") and db.conn is not None:
Expand Down Expand Up @@ -171,4 +171,4 @@ def internal_error(exc):
host="0.0.0.0",
port=int(os.environ.get("PORT", 5000)),
debug=os.environ.get("FLASK_DEBUG", "false").lower() == "true",
)
)
7 changes: 7 additions & 0 deletions api/models/finding.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,13 @@ def _get_conn(self) -> Any:
self.connect()
return self.conn

def close(self) -> None:
"""Close the database connection."""
if self.conn and not self.conn.closed:
self.conn.close()
self.conn = None
logger.debug("Database connection closed")

# ------------------------------------------------------------------ #
# Schema #
# ------------------------------------------------------------------ #
Expand Down
15 changes: 10 additions & 5 deletions api/routes/compliance.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,13 @@


def _get_db() -> DatabaseManager:
if "db_conn" not in g:
g.db_conn = DatabaseManager(os.environ["DATABASE_URL"])
g.db_conn.connect()
return g.db_conn
if "db" not in g:
db_url = os.environ.get("DATABASE_URL")
if not db_url:
raise RuntimeError("DATABASE_URL environment variable is not set")
g.db = DatabaseManager(db_url)
g.db.connect()
return g.db


@compliance_bp.get("/api/compliance/<framework>")
Expand All @@ -41,6 +44,8 @@ def get_compliance(framework: str):
return jsonify(result), 500

return jsonify(result)
except FileNotFoundError as exc:
return jsonify({"error": f"Frameworks directory not found: {exc}"}), 500
except Exception as exc:
logger.error("Failed to retrieve compliance score for %s: %s", framework, exc)
return jsonify({"error": "Compliance calculation failed", "detail": str(exc)}), 500
return jsonify({"error": "Compliance calculation failed", "detail": str(exc)}), 500
34 changes: 22 additions & 12 deletions api/routes/scans.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,22 @@


def _get_db() -> DatabaseManager:
if "db_conn" not in g:
g.db_conn = DatabaseManager(os.environ["DATABASE_URL"])
g.db_conn.connect()
return g.db_conn
if "db" not in g:
db_url = os.environ.get("DATABASE_URL")
if not db_url:
raise RuntimeError("DATABASE_URL environment variable is not set")
g.db = DatabaseManager(db_url)
g.db.connect()
return g.db


@scans_bp.get("/api/scans")
def list_scans():
"""Return all historical scan results ordered by most recent first."""
try:
db = _get_db()
scans = db.get_scans()
return jsonify({"count": len(scans), "scans": scans})
result = db.get_scans()
return jsonify(result)
except Exception as exc:
logger.error("Failed to list scans: %s", exc)
return jsonify({"error": "Failed to retrieve scans", "detail": str(exc)}), 500
Expand All @@ -39,15 +42,20 @@ def trigger_scan():
Note: For production use, replace this with an async task queue (e.g.
Celery or Azure Functions) to avoid request timeouts on large subscriptions.
"""
try:
from scanner.engine import ScanEngine
except ImportError:
return jsonify({"error": "Scanner module is not available"}), 500

try:
body = request.get_json(silent=True) or {}
subscription_id = body.get("subscription_id")
subscription_id = body.get("subscription_id") or os.environ.get(
"AZURE_SUBSCRIPTION_ID"
)

if not subscription_id:
return jsonify({"error": "subscription_id is required"}), 400

from scanner.engine import ScanEngine # deferred — import only after input is validated

logger.info("Scan triggered for subscription %s", subscription_id)

try:
Expand All @@ -57,16 +65,18 @@ def trigger_scan():
logger.error("Scan engine execution failed: %s", exc, exc_info=True)
return jsonify({"error": "Scan failed", "detail": str(exc)}), 500

if not isinstance(result, dict) or "scan_id" not in result:
return jsonify({"error": "Invalid scan result returned"}), 500

try:
db = _get_db()
# Note: Table creation is handled at startup; no need to repeat it here.
db.save_scan(result)
except Exception as exc:
logger.error("Failed to save scan result to database: %s", exc, exc_info=True)
logger.error("Failed to save scan result: %s", exc, exc_info=True)
return jsonify({"error": "Database save failed", "detail": str(exc)}), 500

return jsonify(result), 201

except Exception as exc:
logger.error("Critical error in trigger_scan route: %s", exc, exc_info=True)
return jsonify({"error": "Critical route failure", "detail": str(exc)}), 500
return jsonify({"error": "Critical route failure", "detail": str(exc)}), 500
17 changes: 10 additions & 7 deletions api/routes/score.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,13 @@


def _get_db() -> DatabaseManager:
if "db_conn" not in g:
g.db_conn = DatabaseManager(os.environ["DATABASE_URL"])
g.db_conn.connect()
return g.db_conn
if "db" not in g:
db_url = os.environ.get("DATABASE_URL")
if not db_url:
raise RuntimeError("DATABASE_URL environment variable is not set")
g.db = DatabaseManager(db_url)
g.db.connect()
return g.db


@score_bp.get("/api/score")
Expand All @@ -27,8 +30,8 @@ def get_score():
"""
try:
db = _get_db()
score = db.get_score()
return jsonify({"score": score, "max_score": 100})
result = db.get_score()
return jsonify(result)
except Exception as exc:
logger.error("Failed to calculate score: %s", exc)
return jsonify({"error": "Failed to calculate score", "detail": str(exc)}), 500
return jsonify({"error": "Failed to calculate score", "detail": str(exc)}), 500
5 changes: 5 additions & 0 deletions compliance/frameworks/cis_azure_benchmark.json
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,11 @@
"control_name": "Ensure that 'OS disk' are encrypted",
"description": "Virtual machine OS and data disks are using platform-managed encryption only (EncryptionAtRestWithPlatformKey). CIS 7.2 requires disks to be protected using customer-managed keys or Azure Disk Encryption. Platform-managed encryption does not give the organisation control over the encryption keys and does not satisfy this control."
},
"AZ-CMP-003": {
"control_id": "8.2",
"control_name": "Ensure that 'Endpoint protection solution' is installed on VMs",
"description": "The virtual machine does not have a recognised endpoint protection extension installed. CIS 8.2 requires that an approved endpoint protection solution is installed and running on all virtual machines. Without endpoint protection, malware and ransomware can execute without detection."
},
"AZ-KV-001": {
"control_id": "8.5",
"control_name": "Ensure the Key Vault is Recoverable",
Expand Down
5 changes: 5 additions & 0 deletions compliance/frameworks/iso27001.json
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,11 @@
"control_name": "Policy on the use of cryptographic controls",
"description": "Virtual machine OS and data disks are using platform-managed encryption only (EncryptionAtRestWithPlatformKey). A.10.1.1 requires that a policy on the use of cryptographic controls is developed and implemented. Platform-managed encryption does not give the organisation control over the encryption keys. Customer-managed keys or Azure Disk Encryption are required to satisfy this control."
},
"AZ-CMP-003": {
"control_id": "A.12.2.1",
"control_name": "Controls against malware",
"description": "The virtual machine does not have a recognised endpoint protection extension installed. A.12.2.1 requires that detection, prevention and recovery controls are implemented to protect against malware. Without endpoint protection, malware executing on the VM will not be detected or prevented."
},
"AZ-KV-001": {
"control_id": "A.17.2.1",
"control_name": "Availability of information processing facilities",
Expand Down
5 changes: 5 additions & 0 deletions compliance/frameworks/nist_csf.json
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,11 @@
"control_name": "Data-at-rest is protected",
"description": "Virtual machine OS and data disks are using platform-managed encryption only (EncryptionAtRestWithPlatformKey). PR.DS-1 requires that data at rest is protected using appropriate controls. Platform-managed encryption does not give the organisation control over the encryption keys. Customer-managed keys or Azure Disk Encryption are required to satisfy this control."
},
"AZ-CMP-003": {
"control_id": "DE.CM-4",
"control_name": "Malicious code is detected",
"description": "The virtual machine does not have a recognised endpoint protection extension installed. DE.CM-4 requires that malicious code is detected on organisational systems. Without endpoint protection, malware and ransomware executing on the VM will not be detected or blocked."
},
"AZ-KV-001": {
"control_id": "PR.IP-4",
"control_name": "Backups of information are conducted, maintained, and tested",
Expand Down
5 changes: 5 additions & 0 deletions compliance/frameworks/soc2.json
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,11 @@
"control_name": "Protects Data in Transit and At Rest",
"description": "Virtual machine OS and data disks are using platform-managed encryption only (EncryptionAtRestWithPlatformKey). CC6.7 requires that data is protected using encryption. Platform-managed encryption does not give the organisation control over the encryption keys. Customer-managed keys or Azure Disk Encryption are required to satisfy this control."
},
"AZ-CMP-003": {
"control_id": "CC6.8",
"control_name": "Prevents or Detects Unauthorized or Malicious Software",
"description": "The virtual machine does not have a recognised endpoint protection extension installed. CC6.8 requires that controls are implemented to prevent or detect and act upon the introduction of unauthorized or malicious software. Without endpoint protection, malicious code executing on the VM will not be detected or blocked."
},
"AZ-KV-001": {
"control_id": "A1.2",
"control_name": "Environmental Threats and Recovery",
Expand Down
Loading
Loading