feat(core): v0.4.0 - enterprise rules, OWASP migration, scan hardening, AI foundations, and signed releases - #348
Open
Vishnu2707 wants to merge 195 commits into
Open
Vishnu2707 wants to merge 195 commits into
Vishnu2707 wants to merge 195 commits into
Conversation
… compliance mappings, docs
* feat: add sentinel/ingest.py — Log Analytics ingestion via HMAC-SHA256 * feat: add sentinel/__init__.py * feat: add KQL rule — HIGH severity finding detected * feat: add KQL rule — misconfiguration wave detection * feat: add KQL rule — new resource type critical detection * Delete sentinel/rules directory * Create rules * Delete sentinel/rules * Add KQL rule for high severity findings * Add Misconfiguration Wave detection rule * Add KQL rule for persistent misconfiguration detection * Add KQL rule for new critical resource types This rule identifies new resource types with critical findings that have occurred in the last 24 hours, excluding known types from the last 30 days. * Add script to generate test findings in JSON format This script generates test findings related to security compliance and saves them in a JSON file. * Add Sentinel integration test plan and results Added a comprehensive test plan for Sentinel integration, detailing test objectives, results, and acceptance criteria for various KQL rules and data ingestion. * docs: add sentinel integration setup guide Added a comprehensive setup guide for integrating Sentinel with Azure, covering prerequisites, workspace creation, activation, environment variable setup, ingestion, log verification, KQL rules deployment, and incident verification.
* Add az_net_003.py to check NSG rules for port 443 This script detects Network Security Groups (NSGs) with unrestricted inbound access on port 443 and provides remediation guidance. * Add AZ-NET-004 rule for empty NSG detection This script detects Network Security Groups (NSGs) that have no custom security rules configured, providing details for remediation. * Add AZ-NET-005 rule for DDoS protection check This script detects virtual networks in Azure that do not have DDoS protection enabled and provides remediation steps. * feat: add rule AZ-NET-006 — public IP unassociated with any resource This rule detects public IP addresses that are not associated with any resource, providing details for remediation. * feat: add rule AZ-NET-007 — Application Gateway without WAF enabled This rule detects Application Gateways that do not have WAF enabled, logging findings and providing remediation steps. * feat: add rule AZ-NET-008 — load balancer with no backend pool This rule detects load balancers in Azure that are not configured with a backend pool, indicating potential misconfiguration or unnecessary costs. * feat: add rule AZ-NET-009 — VPN gateway using outdated IKE version This script detects VPN gateways using the outdated IKEv1 protocol and provides remediation steps to migrate to IKEv2. * feat: add rule AZ-NET-010 — subnet with no NSG attached This script detects subnets in Azure that do not have a Network Security Group (NSG) attached, logging findings and providing remediation guidance. * feat: add playbook fix_az_net_003.sh This script updates the NSG rule to restrict inbound traffic on port 443 to a specified IP range. * feat: add playbook fix_az_net_004.sh This script adds a default deny-all inbound rule to a specified NSG. * feat: add playbook fix_az_net_005.sh This script enables DDoS protection on a specified virtual network in Azure. It checks for required parameters and provides usage instructions if they are missing. * feat: add playbook fix_az_net_006.sh This script deletes unassociated public IP addresses in Azure. * feat: add playbook fix_az_net_007.sh This script enables WAF on an Application Gateway, ensuring compliance with the AZ-NET-007 rule. * feat: add playbook fix_az_net_008.sh Script to remediate AZ-NET-008 by deleting empty load balancers. * feat:add script to update VPN connection to IKEv2 This script updates a VPN connection to use IKEv2, ensuring compliance with the AZ-NET-009 rule. * feat: add playbook fix_az_net_010.sh This script attaches a specified network security group to a given subnet in a virtual network, ensuring compliance with the AZ-NET-010 rule. * Clarify description and add note for public-facing services Updated the description to clarify the risk of exposing port 443 and added a note regarding public-facing services. * Change severity level from MEDIUM to HIGH * fix: AZ-NET-005 severity changed to LOW — DDoS Standard high cost on small subscriptions * Add note about NetworkManagementClient usage Added a note regarding the creation of NetworkManagementClient directly and suggested a follow-up for consistency. * Add note about NetworkManagementClient usage Added a note regarding the use of NetworkManagementClient and suggested a follow-up for consistency. * Add additional security controls to CIS Azure benchmark * Refine control descriptions in nist_csf.json Updated descriptions for various controls to enhance clarity and specificity regarding remote access management, data protection, and security measures. * fix: add AZ-NET-003 to AZ-NET-010 to ISO27001 compliance framework Updated descriptions for various controls to clarify compliance requirements and improve security guidance. --------- Co-authored-by: Vishnu Ajith <86302373+Vishnu2707@users.noreply.github.com>
* feat: add rule AZ-STOR-003 storage lifecycle policy check * feat: add rule AZ-STOR-003 storage lifecycle policy check
* docs: add SOC 2 Type II compliance framework mapping for all 20 rules Added SOC 2 Type II framework with detailed controls for security measures and compliance requirements. * feat: add soc2 to FRAMEWORK_FILE_MAP in finding.py add soc2.json to FRAMEWORK_FILE_MAP in finding.py * feat: add soc2 to SUPPORTED_FRAMEWORKS in compliance.py Added 'soc2' to the list of supported compliance frameworks. * Add SOC 2 controls for data protection and management
* refactor: add get_virtual_networks() and get_public_ip_addresses() to AzureClient * Refactor DDoS protection check to use azure_client * refactor: AZ-NET-006 now uses azure_client.get_public_ip_addresses()
- Python syntax check on all rule files - Rule structure validation (RULE_ID, SEVERITY, FRAMEWORKS) + RULE_ID uniqueness - Hardcoded credential scan - Playbook existence + bash syntax check for every rule - Compliance JSON validation for all four framework files (inc. soc2.json) - API syntax check - Compliance vs rule cross-reference check - CI summary step with per-check pass/fail table (if: always) - Fix duplicate DESCRIPTION assignment in az_net_003.py - Add pyyaml to requirements.txt for local YAML validation - Add docs/ci-pipeline.md with local run commands and design rationale - Update CI_PIPELINE_GUIDE.md with final PR description Closes #30
) * fix(api): rate-limit /ready and /metrics, expose db pool telemetry Closes the remaining scope of #296. The connection-checkout leak itself was already fixed by #306 (g.db + the teardown handler, with a real PostgreSQL-backed integration test proving connections balance across repeated readiness probes). What was still open: /ready and /metrics are unauthenticated by design (probe/scrape endpoints must never require a token), which also made them the one place an unauthenticated caller could trigger repeated pooled-connection work with no rate limiting at all, and there was no visibility into how close the pool was to exhaustion before it happened. - Add api.observability.probe_rate_limit: an in-memory, per-process, per-source-IP rate limiter for probe/scrape endpoints. Deliberately not the existing Postgres-backed api.rate_limit.rate_limit, which would add a database round trip (and a second, separately-tracked pooled connection under its own g.db) to the exact endpoint whose job is to protect the database from overload. The connection pool it guards is itself process-local under Gunicorn's multi-worker model, so a per-process budget is the matching granularity, not a weaker substitute for a shared one. Wired onto /ready (budget of 5 per 10s per source IP, half the default DB_POOL_MAX_CONN) and /metrics (20 per 10s, generous for normal Prometheus scrape intervals). The check runs before the view body, so a rejected request never reaches the database work it would otherwise trigger. - Add api.models.finding.get_pool_stats(): a point-in-time snapshot of the shared pool's in-use/idle/max-connection counts and utilization percentage. Reports only counts - never the DSN, host, or credentials - so it's safe on a public surface. Returns zeroed stats before any connection has been made instead of raising. - Wire get_pool_stats() into three new Prometheus gauges (openshield_db_pool_connections_in_use/idle/max), refreshed lazily on every /metrics scrape via a provider callback registered from api/app.py - api/observability.py stays free of project imports (its own documented constraint, since the worker reuses it too) by never importing api.models.finding directly. - Document in docs/deployment/render.md that Render's Blueprint format has no path-based access control, so the in-app rate limiter is a defense-in-depth backstop, not a substitute for restricting network reachability to /ready and /metrics at whatever reverse proxy/CDN/ WAF fronts a real deployment - that configuration is operational, outside what render.yaml can express. New tests in tests/test_readiness_hardening.py cover: the rate limiter's budget/window/per-IP-isolation/testing-bypass/key-pruning behavior in isolation, /ready actually rejecting a source once its budget is spent without touching the database for the rejected request, get_pool_stats()'s zero/nonzero/never-leaks-the-dsn behavior, and /metrics rendering the three new gauges (and surviving a broken stats provider without failing the whole scrape). Verified: full backend suite (796 passed, 3 skipped - pre-existing, unrelated), including tests/test_observability.py's real PostgreSQL-backed readiness-leak test run against a local Postgres instance to confirm the new decorator doesn't disturb #306's fix; ruff check and format --check clean. Signed-off-by: Parth J Rohit <parthrohit60@gmail.com> Signed-off-by: parthrohit22 <parthrohit60@gmail.com> * fix(api): restore dropped constant, bound probe-limiter memory Two fixes on top of the dev merge that landed on this branch: 1. The merge into dev (which now carries #294/#320's own changes to api/app.py) dropped this branch's _READY_MAX_REQUESTS_PER_WINDOW constant definition while keeping its usage on the /ready route, leaving api/app.py with an undefined name that only surfaced at create_app() call time (ruff's F821 caught it as CI's first failure; Backend Tests failed for the same underlying reason). Restored the constant and its comment. 2. m-khan-97's review: probe_rate_limit's per-key cleanup only ever prunes the exact (address, path) key the current request touches. A caller that continuously rotates its source address - or a spoofed forwarded address wherever the trusted-proxy boundary is misconfigured - creates a new one-shot dictionary entry per address that's never revisited and therefore never pruned, making the limiter's own tracking dict an unbounded memory sink. Fixed with the two things asked for: - A periodic global sweep (every _PROBE_SWEEP_INTERVAL calls, not every call - a full-dict scan per request would defeat the point of a cheap in-memory limiter) that prunes every key whose hits have all expired, not just the current request's key. - A hard cap (_PROBE_MAX_TRACKED_KEYS) on distinct tracked keys, with deterministic least-recently-touched eviction via an OrderedDict instead of the previous plain dict - every hit (including one that just survives a sweep) moves its key to the end, so eviction always drops the coldest entry first. Also added the suggested (non-blocking) Retry-After header on 429. New tests in tests/test_readiness_hardening.py cover exactly the scenario m-khan-97 described: many distinct one-shot addresses, advance past the window, trigger cleanup through different addresses, and prove the stale keys are gone and the map stays bounded. Plus direct hard-cap/LRU-eviction tests and the Retry-After header. Verified: full backend suite (862 passed, 5 skipped - pre-existing/ environment-only), including all 17 tests in tests/test_readiness_hardening.py and the pre-existing tests/test_auth.py / tests/test_observability.py suites unaffected. ruff check and format --check clean. Signed-off-by: Parth J Rohit <parthrohit60@gmail.com> Signed-off-by: parthrohit22 <parthrohit60@gmail.com> * fix(api): address TFT444's 3 probe-limiter findings 1. move_to_end(key) ran on every request including rejected ones, so an attacker hammering an already-exhausted key kept it permanently at the back of the eviction order while quiet, legitimate keys drifted toward the front and got evicted instead - defeating the memory cap's actual purpose for exactly the caller it exists to bound. Now only a request that counts against the budget (allowed) refreshes a key's position; a rejected request leaves it wherever it already was. 2. _probe_hits is one dict shared by every probe_rate_limit-decorated endpoint, but the periodic sweep applied whichever endpoint's request happened to trigger it - its own window_seconds - to every tracked key regardless of which endpoint's window actually applies to it. Dormant today since /ready and /metrics both default to the same 10s window, but the first endpoint added with a different one would have caused premature resets or lingering stale entries across every other endpoint's keys. Added _ProbeEntry to carry window_seconds alongside each key's own hits deque, so the sweep (and the per-call prune) always uses the window that key was actually registered under. 3. Documented the ProxyFix(x_for=1) trust boundary explicitly in api/app.py: what it assumes (Render's edge is the only thing able to append to X-Forwarded-For before this process sees it) and what breaks if that's violated (a directly-reachable origin lets a caller set their own forwarded IP per request, which is equivalent to no per-IP rate limiting at all for every control that depends on request.remote_addr). New regression tests for both behavioral fixes: one proves a caller hammering an exhausted key doesn't stay artificially warm while a quiet legitimate key gets evicted in its place; the other runs two endpoints with different window_seconds sharing the tracked-key dict and proves a sweep triggered by one doesn't misapply its window to the other's entries. Verified: full backend suite (864 passed, 5 skipped - pre-existing/ environment-only), all 19 tests in tests/test_readiness_hardening.py including the 2 new ones, ruff check and format --check clean. Signed-off-by: Parth J Rohit <parthrohit60@gmail.com> Signed-off-by: parthrohit22 <parthrohit60@gmail.com> --------- Signed-off-by: Parth J Rohit <parthrohit60@gmail.com> Signed-off-by: parthrohit22 <parthrohit60@gmail.com>
Bumps the npm_and_yarn group with 1 update in the /frontend directory: [postcss-selector-parser](https://github.com/postcss/postcss-selector-parser). Updates `postcss-selector-parser` from 6.1.2 to 6.1.4 - [Release notes](https://github.com/postcss/postcss-selector-parser/releases) - [Changelog](https://github.com/postcss/postcss-selector-parser/blob/main/CHANGELOG.md) - [Commits](postcss/postcss-selector-parser@v6.1.2...6.1.4) --- updated-dependencies: - dependency-name: postcss-selector-parser dependency-version: 6.1.4 dependency-type: indirect dependency-group: npm_and_yarn ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps the npm_and_yarn group with 1 update in the /frontend directory: [browserslist](https://github.com/browserslist/browserslist). Updates `browserslist` from 4.28.2 to 4.28.9 - [Release notes](https://github.com/browserslist/browserslist/releases) - [Changelog](https://github.com/browserslist/browserslist/blob/main/CHANGELOG.md) - [Commits](browserslist/browserslist@4.28.2...4.28.9) --- updated-dependencies: - dependency-name: browserslist dependency-version: 4.28.9 dependency-type: indirect dependency-group: npm_and_yarn ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* feat(frontend): add API request timeout support Signed-off-by: Shaurya K Sharma <shauryaksharma24@gmail.com> * fix(frontend): preserve transient API fallback semantics Signed-off-by: Shaurya K Sharma <shauryaksharma24@gmail.com> --------- Signed-off-by: Shaurya K Sharma <shauryaksharma24@gmail.com>
* website: migrate to Astro + Decap CMS with GitHub Pages pipeline Signed-off-by: ritiksah141 <ritiksah141@gmail.com> * website: commit src/lib, bump astro to patched 7.x, drop obsolete CI job Signed-off-by: ritiksah141 <ritiksah141@gmail.com> * website: build hero legend and rule info with DOM APIs, no innerHTML Signed-off-by: ritiksah141 <ritiksah141@gmail.com> * fix(website): resolve manual test findings across hero, docs, feed and articles Signed-off-by: ritiksah141 <ritiksah141@gmail.com> * feat(website): complete site and automate Pages deployment Signed-off-by: ritiksah141 <ritiksah141@gmail.com> * fix(website): keep CMS optional and resolve CodeQL Signed-off-by: ritiksah141 <ritiksah141@gmail.com> * ci: include website validation in required summary Signed-off-by: ritiksah141 <ritiksah141@gmail.com> * style(ci): keep website gate comment concise Signed-off-by: ritiksah141 <ritiksah141@gmail.com> * fix(ci): preserve container workflow contract Signed-off-by: ritiksah141 <ritiksah141@gmail.com> --------- Signed-off-by: ritiksah141 <ritiksah141@gmail.com>
* feat: complete enterprise data protection rules Signed-off-by: Tanvir Farhad <tamimtarafder12@gmail.com> * fix: satisfy rule validation and refresh image packages Signed-off-by: Tanvir Farhad <tamimtarafder12@gmail.com> * fix(scanner): address review blockers in enterprise data-protection rules - Move AZ-STOR-009 opt-in check from BlobContainer (no ARM tags) to the parent storage account, which exposes tags via the SDK; all containers under a tagged account are now evaluated for immutability. - Replace incorrect NIST mapping A.12.4.1 (ISO 27001) on AZ-DB-007 with PR.PT-1 across az_db_007.py, nist_csf.json, and rules-reference. - Add executable az CLI commands to fix_az_cache_001, fix_az_cosmos_001, fix_az_cosmos_002, fix_az_db_005, fix_az_db_006, and fix_az_db_007 playbooks; each validates the target and requires APPLY confirmation before modifying any Azure resource. - Update storage-protection-controls.md to document the account-level tagging scope for AZ-STOR-009. Signed-off-by: Tanvir Farhad <tamimtarafder12@gmail.com> * fix(scanner): check immutability tag on container, not account (AZ-STOR-009) The policy_required guard was placed at the account level, but the oshield:immutability-required tag is set per container. Moving the check inside the container loop allows containers with the tag to be evaluated regardless of whether the parent account carries it. Signed-off-by: Tanvir Farhad <tamimtarafder12@gmail.com> * fix(scanner): check immutability tag on container or parent account (AZ-STOR-009) The policy_required guard was placed at the account level only, but the oshield:immutability-required tag may be set per-container or per-account. Now uses OR logic: a container is evaluated if the account carries the requirement tag (protecting all containers) OR if the container itself carries it (per-container opt-in). Both cases were previously broken: the account-level check did not reach container-tagged resources, and no per-container check existed at all. Signed-off-by: Tanvir Farhad <tamimtarafder12@gmail.com> * fix(scanner): address storage rule correctness gaps in AZ-STOR-006/007/008 - AZ-STOR-006: treat allow_shared_key_access=None as insecure (Azure documents unset as equivalent to True); only False is compliant - AZ-STOR-007: treat minimum_tls_version=None as TLS 1.0 (Azure default); use enum_str() instead of str() to handle SDK enum objects correctly - AZ-STOR-008 playbook: fix Key Vault URI parsing; the previous bash expansion passed the wrong segments to --encryption-key-vault and --encryption-key-name; now splits vault URI, key name, and optional key version correctly - ci.yml: remove CVE-2026-45830 and CVE-2026-45833 pip-audit exclusions (chromadb CVEs unrelated to this PR; resolved by PR #317) Adds regression tests for None-as-default behavior and SDK enum handling in AZ-STOR-006 and AZ-STOR-007 (22 storage tests, all passing). Signed-off-by: Tanvir Farhad <tamimtarafder12@gmail.com> * fix(scanner): address all review feedback and CI failures for PR #278 Signed-off-by: Tanvir Farhad <tamimtarafder12@gmail.com> --------- Signed-off-by: Tanvir Farhad <tamimtarafder12@gmail.com>
…336) * build: lock Python runtime and development dependencies with hashes Signed-off-by: Muhammad Ibrahim <135441675+m-khan-97@users.noreply.github.com> * fix(ci): isolate lock tooling outside the source checkout Signed-off-by: Muhammad Ibrahim <135441675+m-khan-97@users.noreply.github.com> --------- Signed-off-by: Muhammad Ibrahim <135441675+m-khan-97@users.noreply.github.com>
Signed-off-by: ritiksah141 <ritiksah141@gmail.com>
…attestations (#339) Signed-off-by: Muhammad Ibrahim <135441675+m-khan-97@users.noreply.github.com>
Signed-off-by: ritiksah141 <ritiksah141@gmail.com>
…ion (#294) (#345) * fix(auth): remove dashboard bearer token and add OIDC token verification (#294) Frontend - remove the VITE_JWT_TOKEN bootstrap and dev-local-token fallback - keep bearer tokens in memory only and purge legacy localStorage tokens - fail CI if a JWT-shaped value reaches the public bundle API - move token verification into api/auth.py with two modes: shared_secret (HS256, now also requires sub, optional iss/aud) and oidc (JWKS-verified asymmetric tokens with issuer, audience, expiry, issued-at, subject, tenant allowlist and IdP app-role mapping) - refuse HS256/none in oidc mode, fail closed with 503 when JWKS is unreachable, and refuse to start on incomplete oidc configuration - warn when shared_secret mode runs in production Docs - authentication setup, containment checklist and JWT_SECRET rotation - demo JWT script is now short-lived and for API testing only Signed-off-by: parthrohit22 <parthrohit60@gmail.com> * fix(auth): resolve SAST and credential-scan findings Rename the shared-secret mode constant so the credential scan does not read it as a hardcoded secret, generate the test signing secret at run time, and reword rejection log messages flagged by Semgrep. Signed-off-by: parthrohit22 <parthrohit60@gmail.com> --------- Signed-off-by: parthrohit22 <parthrohit60@gmail.com>
) * fix(scanner): rebase compute-rule corrections onto current dev Signed-off-by: parthrohit22 <parthrohit60@gmail.com> * test(scanner): drop duplicated AZ-CMP-007 test block from bad rebase A stale copy of the AZ-CMP-007 helpers and tests survived the dev rebase, sitting ahead of the AZ-CMP-004 assessment tests. Its local `def _subnet_id(name)` shadowed the module-level `_subnet_id(vnet_name, subnet_name)`, breaking seven AZ-CMP-001 tests with "takes 1 positional argument but 2 were given", and it carried two stray `f[...]` asserts that tripped ruff F811/F821 (11 errors). The canonical AZ-CMP-007 block at end of file (renamed helper `_jit_subnet_id`, no stray asserts) is kept. ruff clean; test_rules_compute.py 56 passed. Signed-off-by: parthrohit22 <parthrohit60@gmail.com> * style(tests): add missing blank line between mock_azure methods `ruff format --check` (run alongside `ruff check` in the Lint job) flagged tests/helpers/mock_azure.py — set_vm_patch_status, added earlier in this PR, had no blank line before it. The earlier `ruff check` F811 failure had masked this. Signed-off-by: parthrohit22 <parthrohit60@gmail.com> --------- Signed-off-by: parthrohit22 <parthrohit60@gmail.com>
…273) * feat: add rule AZ-CMP-005 Trusted Launch (Secure Boot + vTPM) check Adds a Compute scan rule that flags Generation 2 VMs which do not have Trusted Launch fully enabled (security type TrustedLaunch with both Secure Boot and vTPM on). Without them, unsigned or malicious code can run during boot and persist beneath the OS, evading OS-level antimalware and EDR. Generation 1 VMs cannot use Trusted Launch and are treated as NOT_APPLICABLE: a VM's list_all() representation does not carry its Hyper-V generation, so the rule resolves the OS disk's hyper_v_generation to confirm a VM is Gen2 before flagging, and never raises a false finding against Gen1 hardware. A security type already declared as TrustedLaunch is itself Gen2-only, so a Secure-Boot/vTPM-off VM there is flagged without a disk lookup. Confidential VMs (which provide Secure Boot and vTPM by construction) are out of scope, and a VM whose generation cannot be confirmed is left unflagged rather than risking a Gen1 false positive. Includes the remediation playbook (playbooks/cli/fix_az_cmp_005.sh) and maps the rule across the four compliance frameworks: NIST CSF PR.DS-6, ISO 27001 A.12.5.1, and SOC 2 CC6.8. CIS uses the repository's existing N/A convention (as in AZ-KV-001) because CIS Azure Foundations 2.0.0 has no dedicated Trusted Launch recommendation. Adds six unit tests covering the compliant, non-compliant Gen2, TrustedLaunch-declared-but-vTPM-off, Gen1 NOT_APPLICABLE, unknown-generation, and Confidential-VM cases. Closes #269 Signed-off-by: shariqueahmad108-ship-it <shariqueahmad108@gmail.com> * fix(playbook): guard fix_az_cmp_005.sh args under set -u Use ${1:-}/${2:-} so running the playbook with missing arguments prints the usage message and exits 1, instead of crashing on an unbound variable under `set -euo pipefail`. Matches the convention in fix_az_net_016.sh. Addresses review feedback from @Vishnu2707 on #273. Signed-off-by: shariqueahmad108-ship-it <shariqueahmad108@gmail.com> --------- Signed-off-by: shariqueahmad108-ship-it <shariqueahmad108@gmail.com>
* fix(scanner): resolve COR-001-004 scanner correctness issues (#151) Signed-off-by: safidnadaf <safidnadaf25@gmail.com> * fix(scanner): normalise Azure SDK enum fields in AZ-NET-003 and AZ-DB-002 Addresses review feedback on PR #163: - Add a shared enum_str() helper in azure_client.py that safely unwraps Azure SDK enum fields via .value, since str(enum_member) yields e.g. 'SecurityRuleDirection.INBOUND' rather than 'Inbound' and silently breaks naive string comparisons against real SDK objects. - AZ-NET-003: normalise direction, access, and source_address_prefix through enum_str() so real SecurityRuleDirection/SecurityRuleAccess enum values are detected correctly, not just plain-string mocks. - AZ-DB-002: normalise the auditing policy state through enum_str() so a real BlobAuditingPolicyState.ENABLED value is not mistaken for disabled (false positive) or vice versa. - AZ-DB-002: malformed ARM IDs are now logged explicitly instead of silently skipped. - AZ-NET-003: the matched plural source_address_prefixes entry is now included in finding metadata. - Add regression tests using real azure-mgmt-network / azure-mgmt-sql SDK model classes (SecurityRule, SecurityRuleDirection, SecurityRuleAccess, ServerBlobAuditingPolicy, BlobAuditingPolicyState) rather than only SimpleNamespace/string-backed mocks, per SHAURYAKSHARMA24's review. - Sync branch with upstream dev (v0.3.0) and apply current ruff format gate, per ritiksah141's review. Signed-off-by: safidnadaf <safidnadaf25@gmail.com> * fix: remove duplicate _diagnostic_settings init line Signed-off-by: safidnadaf <safidnadaf25@gmail.com> * test: add identity rule regression coverage Signed-off-by: safidnadaf <safidnadaf25@gmail.com> * fix: improve AZ-NET-003 scanner correctness Signed-off-by: safidnadaf <safidnadaf25@gmail.com> * fix: resolve ruff lint errors Signed-off-by: safidnadaf <safidnadaf25@gmail.com> * test: restore az_net_016/017 imports dropped in merge Signed-off-by: safidnadaf <safidnadaf25@gmail.com> * chore: trigger CI Signed-off-by: safidnadaf <safidnadaf25@gmail.com> * fix: add compliant plural-port test, drop unrelated identity test reformatting Addresses parthrohit22's review: adds a compliant-case regression test for the destination_port_ranges fix in AZ-NET-003, and resets tests/test_rules_identity.py to dev's formatting, keeping only the one genuine new test (test_idn_007_disabled_user_without_mfa_returns_no_findings). Signed-off-by: safidnadaf <safidnadaf25@gmail.com> * style: apply ruff format to az_net_003.py Signed-off-by: safidnadaf <safidnadaf25@gmail.com> * fix(scanner): AZ-DB-002 emits indeterminate LOW finding on failed policy lookup Addresses TFT444's review: previously a failed auditing-policy lookup (API/auth failure) was silently skipped. This aligns with the AZ-CMP-002 convention by emitting a LOW-severity finding with metadata.determination = 'indeterminate' instead, so the verification gap stays visible in scan output rather than disappearing silently. Signed-off-by: safidnadaf <safidnadaf25@gmail.com> --------- Signed-off-by: safidnadaf <safidnadaf25@gmail.com> Co-authored-by: safidnadaf <safidnadaf@users.noreply.github.com>
* feat: add VM Scale Set inventory collector and rule AZ-CMP-005 Adds AzureClient.get_virtual_machine_scale_sets() (list_all across the subscription, following the get_virtual_machines() pattern) and its MockAzureClient test double. Ships AZ-CMP-005 as the first rule to use it: flags VMSS network interface configurations that provision a public IP with no NSG attached, the VMSS-template equivalent of AZ-CMP-001's per-VM NIC check. Detection reads the network interface configuration template directly (network_interface_configurations[].ip_configurations[] / .network_security_group) rather than resolving separate NIC resources, since a VMSS profile embeds these settings inline. Includes a remediation playbook (az vmss update --set on the network profile, with an explicit warning about the required instance upgrade), collector and rule tests, and compliance framework mappings. CIS is mapped to N/A-CMP-005 following the repository's established convention for the same real control (7.1, owned by AZ-CMP-001) applied to a second resource type, since the one-CIS-ID-per-rule convention doesn't allow reusing 7.1 directly. Closes #271 Signed-off-by: Dipesh Ray <dipesh.ray.g@gmail.com> * fix: renumber AZ-CMP-005 to AZ-CMP-006, fix subnet-NSG false positive AZ-CMP-005 collided with #273 (Trusted Launch check), opened a day before this PR and already claiming that rule ID. Renumbered the rule file, playbook, tests, and all four compliance framework entries to AZ-CMP-006. Also fixes a false-positive gap flagged in review: the rule only checked for an NSG on the VMSS network interface configuration itself, missing the case where the NSG is attached at the subnet level instead. A VMSS whose NIC has no NSG but deploys into a subnet that does have one was being incorrectly flagged. Now resolves each network interface configuration's subnet (via the existing get_virtual_networks() collector, no new collector needed) and treats either a NIC-level or subnet-level NSG as compliant, matching how AZ-NET-010 already reads subnet.network_security_group. Added SOC2 to the rule's own FRAMEWORKS dict (was previously only in soc2.json, inconsistent with how several other rules, e.g. AZ-KV-006, already include it directly). Addresses review feedback from TFT444 and m-khan-97 on #275. Signed-off-by: Dipesh Ray <dipesh.ray.g@gmail.com> * fix: distinguish unresolved subnet from confirmed no-NSG in AZ-CMP-006 subnet_nsgs.get(subnet_id, False) treated an unresolved subnet reference (VNet collection failure, missing permissions, or a subnet ID this scan never saw) identically to a resolved subnet confirmed to have no NSG, reintroducing the false-positive the previous commit was meant to fix. Now distinguishes three states per ip_configuration: resolved+has NSG (compliant), resolved+no NSG (confirmed non-compliant, HIGH), and unresolved (indeterminate, LOW), mirroring the confirmed/indeterminate pattern already established in az_cmp_002.py. Also normalizes subnet IDs to lowercase before comparison, since Azure resource IDs are case-insensitive and the two APIs involved (VMSS network profile vs. VNet subnets) aren't guaranteed to return matching casing. Adds regression tests for an unresolved subnet (VNet collection returns empty) and for a differently-cased subnet ID match. Addresses review feedback from ritiksah141 on #275. Signed-off-by: Dipesh Ray <dipesh.ray.g@gmail.com> * fix: resolve _subnet_id name collision from rebasing onto AZ-CMP-007 AZ-CMP-007 (merged into dev while this branch was in review) and this branch's subnet-NSG fix for AZ-CMP-006 each defined their own _subnet_id() helper with different signatures. Landing them in the same file after the rebase left two same-named functions, and the second definition silently shadowed the first, breaking the AZ-CMP-007 subnet-exposure test. Renamed this branch's helper to _vnet_subnet_id() to remove the collision. Signed-off-by: Dipesh Ray <dipesh.ray.g@gmail.com> * fix: three real gaps in AZ-CMP-006 flagged by review - Only ip_configurations that actually carry a public IP are checked against subnet NSG state. A non-primary ip_config with no public IP is not internet-reachable, so its subnet must not be able to force a finding on an otherwise-compliant net_config. - Removed the break after the first non-compliant net_config. A VMSS with several exposed configs now gets one finding per config instead of silently hiding every attack surface after the first. - Indeterminate findings now carry vnets_collected in metadata, so a persistent zero across many findings is visible as a VNet-collection problem instead of reading as an ordinary per-subnet indeterminate result. Signed-off-by: Dipesh Ray <dipesh.ray.g@gmail.com> * fix: require all public ip_configs protected, not any, in AZ-CMP-006 A net_config can carry several public ip_configs on different subnets. 'any(status is True for status in subnet_statuses)' treated the whole net_config as compliant if any one of them was subnet -protected, even when another public ip_config on the same net_config was still exposed on an unprotected subnet. Compliance now requires every public ip_config to be protected. A confirmed-unprotected ip_config makes the net_config non-compliant regardless of whether another ip_config on it is merely unresolved, matching the existing confirmed-beats-indeterminate severity model. Added a regression test: two public ip_configs on different subnets, one protected and one not, expecting one confirmed HIGH finding. Signed-off-by: Dipesh Ray <dipesh.ray.g@gmail.com> --------- Signed-off-by: Dipesh Ray <dipesh.ray.g@gmail.com>
* Add enterprise AKS and workload security controls Signed-off-by: ritiksah141 <ritiksah141@gmail.com> * Address AKS security review feedback Signed-off-by: ritiksah141 <ritiksah141@gmail.com> * Harden AKS tenant evidence isolation Signed-off-by: ritiksah141 <ritiksah141@gmail.com> * Fix trusted registry prefix boundaries Signed-off-by: ritiksah141 <ritiksah141@gmail.com> * Fix AKS policy namespace exclusions Signed-off-by: ritiksah141 <ritiksah141@gmail.com> * Fix Linux dependency lock completeness Signed-off-by: ritiksah141 <ritiksah141@gmail.com> * Clarify AKS secret remediation guidance Signed-off-by: ritiksah141 <ritiksah141@gmail.com> --------- Signed-off-by: ritiksah141 <ritiksah141@gmail.com> Co-authored-by: Vishnu Ajith <86302373+Vishnu2707@users.noreply.github.com> Co-authored-by: Muhammad Ibrahim <135441675+m-khan-97@users.noreply.github.com>
* fix(deps): bump pip to 26.2.1 (Dependabot alert #21) pip < 26.2.0 would incorrectly handle doubly-encoded package URLs from indexes, allowing a malicious index to serve unexpected packages. This updates requirements-lock.txt to pin pip==26.2.1 with the correct hash. Fixes: https://github.com/OWASP/openshield/security/dependabot/21 Signed-off-by: Tanvir Farhad <tamimtarafder12@gmail.com> * fix(deps): update lock_dependencies.py pip version guard to 26.2.1 Signed-off-by: Tanvir Farhad <tamimtarafder12@gmail.com> * fix(deps): upgrade pip-tools to 7.6.1 for pip 26.2.1 compatibility pip-tools 7.5.3 imports stdlib_pkgs from pip._internal.utils.compat, which was removed in pip 26.2.1. pip-tools 7.6.1 dropped that internal dependency (fixed in 7.4.1). Update the source pin in requirements-lock.in, the compiled hash in requirements-lock.txt, and the version guard in lock_dependencies.py to keep all three in sync. Signed-off-by: Tanvir Farhad <tamimtarafder12@gmail.com> --------- Signed-off-by: Tanvir Farhad <tamimtarafder12@gmail.com>
Vishnu2707
requested review from
SHAURYAKSHARMA24,
parthrohit22,
ritiksah141 and
vogonPrayas
as code owners
September 20, 2026 20:18
…rding (#350) * fix(ci): update learn-page script patterns to match current README wording - feature_row: add 'Kubernetes workloads' between AKS and post-quantum - playbook_row: match 'Every documented rule ships with a matching review-gated remediation script' (was 'Azure CLI remediation script') - Add CRITICAL severity box to severity distribution grid in learn page - Expand grid from 3 to 4 columns to accommodate the new CRITICAL box - Update chart_severities set and render() signature to track CRITICAL Signed-off-by: Tanvir Farhad <tamimtarafder12@gmail.com> * fix(ci): fix E501 line-too-long and resolve engine.py merge conflict Signed-off-by: Tanvir Farhad <tamimtarafder12@gmail.com> * fix(ci): shorten print line to satisfy E501 (120 char limit) Signed-off-by: Tanvir Farhad <tamimtarafder12@gmail.com> --------- Signed-off-by: Tanvir Farhad <tamimtarafder12@gmail.com>
…ndpoint (#327) * feat(scanner): add rule AZ-STOR-010 storage account missing private endpoint Adds a Storage rule that flags storage accounts reachable over the public network that have no approved Private Endpoint connection, so their blob/file/queue/table endpoints stay reachable from the internet instead of staying on a private IP inside a VNet. Detection reads the real azure-mgmt-storage model shape: an account is flagged when private_endpoint_connections has no entry whose private_link_service_connection_state.status is "Approved". An account whose public_network_access is already "Disabled" is treated as NOT_APPLICABLE (network-isolated by another means), so the rule does not raise a false finding. Includes the remediation playbook (creates a blob Private Endpoint and sets public network access to Disabled; args guarded per the fix_az_net_016.sh convention), four unit tests (approved / pending-only / none / public-disabled) that exercise genuine SDK models, and framework mappings: NIST PR.AC-5, ISO 27001 A.13.1.3, SOC 2 CC6.6, and the repo's N/A convention for CIS. Resolves #322. The issue proposed the id AZ-STOR-007, but that id is already in use (TLS below 1.2) and the storage rules run through AZ-STOR-009, so this lands as AZ-STOR-010. Signed-off-by: shariqueahmad108-ship-it <shariqueahmad108@gmail.com> * fix(AZ-STOR-010): treat unavailable private-endpoint evidence as indeterminate; document the rule Addresses review feedback on #327: - Indeterminate evidence: private_endpoint_connections of None means the field was not populated / could not be read, not a confirmed absence. The rule now skips such an account (logging a warning) instead of flagging it, so it does not raise a false finding from missing evidence. A genuine empty list, or connections with none in the Approved state, is still a finding. - Adds a regression test for the None (unavailable) case. - Documents AZ-STOR-010 in docs/rules-reference.md. Signed-off-by: shariqueahmad108-ship-it <shariqueahmad108@gmail.com> --------- Signed-off-by: shariqueahmad108-ship-it <shariqueahmad108@gmail.com>
…ingest and RAG pipeline (#174) * test: add missing coverage for DatabaseManager, API routes, Sentinel ingest and RAG pipeline - DatabaseManager: tests for init, DSN handling, Finding dataclass, SEVERITY_WEIGHTS and get_score() SQL logic - API routes: tests for score, compliance, drift, resources and prioritization endpoints - Sentinel ingest: tests for HMAC-SHA256 signature, field mappings, retry count and early exit - RAG pipeline: tests for loader, chunker overlap, retriever error handling and embed.py pipeline - All tests passing, ruff check and format clean Refs #153 Signed-off-by: Mahfuzur Rahman Emon <mahfuzur.emon01@gmail.com> * test: add non-empty drift and resources tests, remove unused helper, rebase from dev - Drift: add test_drift_with_two_scans_classifies_added_removed with 2 scans that differ so ADDED/REMOVED classification is actually exercised - Resources: add test_resources_with_mixed_severity_rows with risk_rank 3/2/1 rows so rank_to_risk mapping and by_risk_level counts are verified - Remove unused _make_db helper from test_database_manager.py - Rebased from dev (86 tests: 24 sentinel + 24 RAG + 14 DB + 24 routes) Refs #153 Signed-off-by: Mahfuzur Rahman Emon <mahfuzur.emon01@gmail.com> * fix: update sentinel tests for validation and timestamp format changes in dev Signed-off-by: Mahfuzur Rahman Emon <mahfuzur.emon01@gmail.com> * fix: strengthen drift ADDED/REMOVED assertions, fix sentinel unknown severity test, remove cbom.py and duplicate SEVERITY_WEIGHTS tests - Drift test now asserts summary[added]==1, summary[removed]==1, type==ADDED/REMOVED and rule_violated==AZ-NET-001 - Sentinel unknown severity test now verifies ValidationError is raised for unknown severity - cbom.py removed from diff (restored to upstream) - Duplicate SEVERITY_WEIGHTS tests removed from TestScoreCalculation - Prioritization silent guard replaced with hard assertion - _make_prioritization_db fixed to handle fetchone side_effect correctly Refs #153 Signed-off-by: Mahfuzur Rahman Emon <mahfuzur.emon01@gmail.com> * fix: update tests for OWASP repo changes - SEVERITY_WEIGHTS moved, retriever/embed rewritten to BM25 - test_database_manager: import SEVERITY_WEIGHTS from openshield.severity - test_rag_pipeline: update retriever tests for BM25 (no chromadb), update embed tests for BM25 - test_api_routes: fix prioritization mock to handle fetchall side_effect for rules+severity counts Refs #153 Signed-off-by: Mahfuzur Rahman Emon <mahfuzur.emon01@gmail.com> * fix: remove unused imports in test_rag_pipeline Signed-off-by: Mahfuzur Rahman Emon <mahfuzur.emon01@gmail.com> * fix: update sentinel tests for OWASP mandatory severity validation Signed-off-by: Mahfuzur Rahman Emon <mahfuzur.emon01@gmail.com> * fix: use pytest.raises(ValidationError) for unknown severity test Signed-off-by: Mahfuzur Rahman Emon <mahfuzur.emon01@gmail.com> * fix: add missing pytest import to test_sentinel_ingest Signed-off-by: Mahfuzur Rahman Emon <mahfuzur.emon01@gmail.com> * test: harden build_vectorstore + compliance-route tests; drop unrelated cbom change - test_rag_pipeline: build_vectorstore test now patches VECTORSTORE_DIR and INDEX_PATH to a real temp dir, asserts the index file is written, and drops the broad try/except that could hide index-write failures. - test_api_routes: compliance tests now assert get_compliance_score() is called with the exact framework and validate the response payload (framework echoed, required keys present, passed+failed==total) for all six supported frameworks (cis, nist, iso27001, soc2, ncsc_pqc, enisa_pqc); plus an error-result -> 500 case. - cbom.py: reverted the unrelated 'from __future__ import annotations' change (now identical to dev). Refs #153 Signed-off-by: Mahfuzur Rahman Emon <mahfuzur.emon01@gmail.com> --------- Signed-off-by: Mahfuzur Rahman Emon <mahfuzur.emon01@gmail.com>
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What does this PR do?
Promotes dev to main for the v0.4.0 release. Captures all work since v0.3.0 — enterprise rule packs, OWASP project migration, scan durability hardening, AI layer foundations, signed releases, supply chain security, frontend accessibility, and dependency security fixes.
Type of change
Key changes
Testing
Related issues
Closes #263, #303
Checklist