Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 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
0e82402
docs: add security policy, issue template, and README badges (#64)
ritiksah141 May 24, 2026
1b25a74
feat: add rule AZ-KV-004 Key Vault purge protection disabled (#55)
aav-wh 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
69 changes: 69 additions & 0 deletions .github/ISSUE_TEMPLATE/feature_request.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
name: Feature Request
about: Suggest a new rule, compliance mapping, playbook, or capability for OpenShield
title: "feat: "
labels: enhancement
assignees: ''
---

## Summary

A clear one-sentence description of the feature you are proposing.

## Problem It Solves

What is the current limitation or gap? Why does this matter for Azure cloud security posture?
Link any related issues or discussions if relevant.

## Proposed Solution

Describe what you want to happen. Be specific.

---

**If proposing a new scanner rule, fill in all fields below:**

- Azure resource type:
- Misconfiguration it detects:
- Suggested RULE_ID (format: `AZ-<SERVICE>-<NNN>`, e.g. `AZ-KV-003`):
- Severity: (CRITICAL / HIGH / MEDIUM / LOW)
- Compliance frameworks it maps to:
- CIS Azure Benchmark control:
- NIST CSF control:
- ISO 27001 control:
- Does a matching remediation playbook need to be created? (Yes / No)

---

**If proposing a compliance mapping:**

- Framework name and version:
- Control ID(s):
- Which existing rules does it apply to:
- Source documentation link:

---

**If proposing an API or CLI change:**

- Endpoint or command affected:
- Current behaviour:
- Proposed behaviour:
- Example request/response or command:

---

## Alternatives Considered

What other approaches did you consider, and why did you rule them out?

## Additional Context

Add any Azure documentation links, CVE references, CIS Benchmark pages, screenshots, or reference implementations here.

## Contribution

Are you willing to implement this yourself?

- [ ] Yes, I plan to open a PR for this
- [ ] I can help review a PR but cannot implement it myself
- [ ] I am not able to contribute code for this
82 changes: 82 additions & 0 deletions .github/SECURITY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# Security Policy

## Reporting a Vulnerability

If you discover a security vulnerability in OpenShield, please **do not open a public GitHub issue**.
Opening a public issue exposes the vulnerability to bad actors before a fix is available.


We will acknowledge your report within 48 hours and work with you to coordinate a fix and responsible disclosure timeline.

### What to include in your report

To help us triage quickly, please include:

- A description of the vulnerability and its potential impact
- The affected component (scanner engine, REST API, auth logic, playbooks)
- Steps to reproduce the issue
- Any relevant logs, proof-of-concept code, or screenshots
- The version of OpenShield you were testing (check `git log --oneline -1`)

The more detail you provide, the faster we can respond.

---

## Supported Versions

| Version | Supported |
|---------|-----------|
| 0.1.x | Yes |

Older versions are not patched. If you are running a version below 0.1.x, upgrade to the latest release before filing a report.

---

## Disclosure Process

We follow a coordinated disclosure model:

1. **Report received** -- you email the vulnerability privately
2. **Acknowledgement** -- we respond within 48 hours to confirm receipt
3. **Investigation** -- we reproduce and assess the impact
4. **Fix developed** -- we write and test a patch
5. **Coordinated release** -- we agree a disclosure date with you (typically 7-14 days after fix)
6. **Public advisory** -- we publish a GitHub Security Advisory and release the fix

We ask that you do not publicly disclose the vulnerability until step 6 is complete.

---

## Scope

### In scope

- Scanner engine (`scanner/`) -- rule logic, Azure SDK calls, output handling
- REST API (`api/`) -- authentication, authorisation, input validation, JWT handling
- Compliance framework mappings (`compliance/`) -- data integrity
- Sentinel integration (`sentinel/`) -- HMAC signing, data upload logic
- Hardcoded secrets or credentials anywhere in the codebase

### Out of scope

- Vulnerabilities in third-party dependencies -- report those to the upstream maintainer
- Security issues in infrastructure you deploy OpenShield to (your Azure environment, your PostgreSQL instance)
- Social engineering attacks
- Physical security

---

## Recognition

We value responsible disclosure. Researchers who report valid vulnerabilities will be:

- Acknowledged by name (or pseudonym if preferred) in the release notes for the fix
- Listed in a `SECURITY_ACKNOWLEDGEMENTS.md` file we maintain in this repository

We do not currently offer a bug bounty programme, but we are grateful for every report.

---

## Contact

**Email: vishnu.ajith@owasp.org**
28 changes: 25 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
# OpenShield

> **Open source Cloud Security Posture Management (CSPM) for Azure — built by the community, for the community.**

[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
> **Open source Cloud Security Posture Management (CSPM) for Azure - built by the community, for the community.**

[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
[![Python 3.11](https://img.shields.io/badge/python-3.11-blue.svg)](https://www.python.org/downloads/release/python-3110/)
[![CI](https://github.com/openshield-org/openshield/actions/workflows/ci.yml/badge.svg?branch=dev)](https://github.com/openshield-org/openshield/actions/workflows/ci.yml)
[![Deploy](https://github.com/openshield-org/openshield/actions/workflows/deploy.yml/badge.svg?branch=dev)](https://github.com/openshield-org/openshield/actions/workflows/deploy.yml)
[![Security Policy](https://img.shields.io/badge/security-policy-green.svg)](.github/SECURITY.md)
[![OWASP](https://img.shields.io/badge/OWASP-listing%20review-orange.svg)](https://owasp.org)
[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](CONTRIBUTING.md)
[![Good First Issues](https://img.shields.io/github/issues/openshield-org/openshield/good-first-issue)](https://github.com/openshield-org/openshield/issues?q=is%3Aissue+label%3Agood-first-issue)
[![Discord](https://img.shields.io/badge/Discord-Join%20Us-7289da)](https://discord.gg/openshield)
Expand Down Expand Up @@ -113,6 +118,7 @@ openshield/

---


## Quick Start

```bash
Expand Down Expand Up @@ -185,4 +191,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
10 changes: 10 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 Expand Up @@ -127,6 +132,11 @@
"control_id": "4.3.6",
"control_name": "Ensure SSL connection is enabled for PostgreSQL Flexible Server",
"description": "SSL enforcement should be enabled on PostgreSQL Flexible Server to ensure data in transit is encrypted. Without SSL, database connections transmit data in plaintext, exposing it to interception."
},
"AZ-KV-004": {
"control_id": "8.6",
"control_name": "Ensure that Azure Key Vault Purge Protection is Enabled",
"description": "Azure Key Vaults without purge protection enabled allow permanent deletion of vaults and their secrets, keys, and certificates during the soft-delete retention period. Even with soft delete enabled, a malicious insider or privileged account can purge vault objects before the retention period expires. Enabling purge protection prevents this by blocking purge operations for the full retention period."
}
}
}
Loading
Loading