mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-07-05 15:29:01 +03:00
Initial commit - 611 cybersecurity skills across all subdomains
This commit is contained in:
@@ -0,0 +1,234 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Google Workspace SSO Configuration Validator
|
||||
|
||||
Validates SAML SSO configuration between an IdP and Google Workspace by
|
||||
checking SAML metadata, testing authentication flows, and verifying
|
||||
certificate validity.
|
||||
|
||||
Requirements:
|
||||
pip install requests cryptography lxml
|
||||
"""
|
||||
|
||||
import base64
|
||||
import json
|
||||
import sys
|
||||
import zlib
|
||||
from datetime import datetime, timezone
|
||||
from urllib.parse import urlencode, parse_qs, urlparse
|
||||
|
||||
try:
|
||||
import requests
|
||||
from cryptography import x509
|
||||
from cryptography.hazmat.primitives import serialization
|
||||
except ImportError:
|
||||
print("[ERROR] Required: pip install requests cryptography")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
class GoogleWorkspaceSSOValidator:
|
||||
"""Validate Google Workspace SAML SSO configuration."""
|
||||
|
||||
def __init__(self, domain):
|
||||
self.domain = domain
|
||||
self.acs_url = f"https://www.google.com/a/{domain}/acs"
|
||||
self.entity_id = f"google.com/a/{domain}"
|
||||
|
||||
def validate_idp_metadata(self, metadata_url):
|
||||
"""Fetch and validate IdP SAML metadata XML."""
|
||||
try:
|
||||
from lxml import etree
|
||||
except ImportError:
|
||||
return {"valid": False, "error": "lxml required for XML parsing"}
|
||||
|
||||
try:
|
||||
resp = requests.get(metadata_url, timeout=15)
|
||||
resp.raise_for_status()
|
||||
except requests.RequestException as e:
|
||||
return {"valid": False, "error": f"Cannot fetch metadata: {e}"}
|
||||
|
||||
try:
|
||||
root = etree.fromstring(resp.content)
|
||||
except etree.XMLSyntaxError as e:
|
||||
return {"valid": False, "error": f"Invalid XML: {e}"}
|
||||
|
||||
ns = {
|
||||
"md": "urn:oasis:names:tc:SAML:2.0:metadata",
|
||||
"ds": "http://www.w3.org/2000/09/xmldsig#",
|
||||
}
|
||||
|
||||
entity_id = root.get("entityID")
|
||||
sso_elements = root.findall(
|
||||
".//md:IDPSSODescriptor/md:SingleSignOnService", ns
|
||||
)
|
||||
sso_urls = [
|
||||
{"binding": el.get("Binding"), "location": el.get("Location")}
|
||||
for el in sso_elements
|
||||
]
|
||||
|
||||
cert_elements = root.findall(
|
||||
".//md:IDPSSODescriptor/md:KeyDescriptor/ds:KeyInfo/ds:X509Data/ds:X509Certificate",
|
||||
ns,
|
||||
)
|
||||
certificates = [el.text.strip() for el in cert_elements if el.text]
|
||||
|
||||
return {
|
||||
"valid": True,
|
||||
"entity_id": entity_id,
|
||||
"sso_endpoints": sso_urls,
|
||||
"certificate_count": len(certificates),
|
||||
"has_post_binding": any(
|
||||
"HTTP-POST" in s["binding"] for s in sso_urls
|
||||
),
|
||||
"has_redirect_binding": any(
|
||||
"HTTP-Redirect" in s["binding"] for s in sso_urls
|
||||
),
|
||||
}
|
||||
|
||||
def validate_certificate(self, cert_pem):
|
||||
"""Validate the IdP signing certificate."""
|
||||
try:
|
||||
if "-----BEGIN CERTIFICATE-----" not in cert_pem:
|
||||
cert_pem = (
|
||||
"-----BEGIN CERTIFICATE-----\n"
|
||||
+ cert_pem
|
||||
+ "\n-----END CERTIFICATE-----"
|
||||
)
|
||||
|
||||
cert = x509.load_pem_x509_certificate(cert_pem.encode())
|
||||
now = datetime.now(timezone.utc)
|
||||
|
||||
return {
|
||||
"valid": True,
|
||||
"subject": cert.subject.rfc4514_string(),
|
||||
"issuer": cert.issuer.rfc4514_string(),
|
||||
"not_before": cert.not_valid_before_utc.isoformat(),
|
||||
"not_after": cert.not_valid_after_utc.isoformat(),
|
||||
"is_expired": now > cert.not_valid_after_utc,
|
||||
"days_until_expiry": (cert.not_valid_after_utc - now).days,
|
||||
"serial_number": str(cert.serial_number),
|
||||
"signature_algorithm": cert.signature_algorithm_oid._name,
|
||||
"key_size": cert.public_key().key_size,
|
||||
}
|
||||
except Exception as e:
|
||||
return {"valid": False, "error": str(e)}
|
||||
|
||||
def validate_sso_configuration(self, idp_sso_url, idp_entity_id,
|
||||
cert_pem):
|
||||
"""Run complete SSO configuration validation."""
|
||||
results = {
|
||||
"domain": self.domain,
|
||||
"acs_url": self.acs_url,
|
||||
"entity_id": self.entity_id,
|
||||
"checks": [],
|
||||
}
|
||||
|
||||
# Check 1: ACS URL format
|
||||
results["checks"].append({
|
||||
"check": "ACS URL format",
|
||||
"expected": f"https://www.google.com/a/{self.domain}/acs",
|
||||
"status": "PASS",
|
||||
})
|
||||
|
||||
# Check 2: Entity ID format
|
||||
results["checks"].append({
|
||||
"check": "Entity ID format",
|
||||
"expected": f"google.com/a/{self.domain}",
|
||||
"status": "PASS",
|
||||
})
|
||||
|
||||
# Check 3: IdP SSO URL is HTTPS
|
||||
is_https = idp_sso_url.startswith("https://")
|
||||
results["checks"].append({
|
||||
"check": "IdP SSO URL uses HTTPS",
|
||||
"value": idp_sso_url,
|
||||
"status": "PASS" if is_https else "FAIL",
|
||||
})
|
||||
|
||||
# Check 4: IdP SSO URL is reachable
|
||||
try:
|
||||
resp = requests.head(idp_sso_url, timeout=10, allow_redirects=True)
|
||||
reachable = resp.status_code < 500
|
||||
except requests.RequestException:
|
||||
reachable = False
|
||||
results["checks"].append({
|
||||
"check": "IdP SSO URL reachable",
|
||||
"status": "PASS" if reachable else "FAIL",
|
||||
})
|
||||
|
||||
# Check 5: Certificate validation
|
||||
cert_result = self.validate_certificate(cert_pem)
|
||||
results["checks"].append({
|
||||
"check": "IdP certificate valid",
|
||||
"status": "PASS" if cert_result.get("valid") and not cert_result.get("is_expired") else "FAIL",
|
||||
"details": cert_result,
|
||||
})
|
||||
|
||||
# Check 6: Certificate expiry warning
|
||||
days_left = cert_result.get("days_until_expiry", 0)
|
||||
results["checks"].append({
|
||||
"check": "Certificate expiry > 30 days",
|
||||
"days_remaining": days_left,
|
||||
"status": "PASS" if days_left > 30 else "WARN",
|
||||
})
|
||||
|
||||
# Check 7: Key size
|
||||
key_size = cert_result.get("key_size", 0)
|
||||
results["checks"].append({
|
||||
"check": "Certificate key size >= 2048",
|
||||
"key_size": key_size,
|
||||
"status": "PASS" if key_size >= 2048 else "FAIL",
|
||||
})
|
||||
|
||||
all_pass = all(
|
||||
c["status"] in ("PASS", "WARN") for c in results["checks"]
|
||||
)
|
||||
results["overall_status"] = "PASS" if all_pass else "FAIL"
|
||||
|
||||
return results
|
||||
|
||||
def generate_saml_authn_request(self, idp_sso_url):
|
||||
"""Generate a SAML AuthnRequest for testing SP-initiated SSO."""
|
||||
request_id = f"_google_workspace_test_{int(datetime.now().timestamp())}"
|
||||
issue_instant = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
|
||||
|
||||
authn_request = f"""<samlp:AuthnRequest
|
||||
xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
|
||||
xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
|
||||
ID="{request_id}"
|
||||
Version="2.0"
|
||||
IssueInstant="{issue_instant}"
|
||||
AssertionConsumerServiceURL="{self.acs_url}"
|
||||
Destination="{idp_sso_url}"
|
||||
ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST">
|
||||
<saml:Issuer>{self.entity_id}</saml:Issuer>
|
||||
<samlp:NameIDPolicy
|
||||
Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
|
||||
AllowCreate="true"/>
|
||||
</samlp:AuthnRequest>"""
|
||||
|
||||
compressed = zlib.compress(authn_request.encode())[2:-4]
|
||||
encoded = base64.b64encode(compressed).decode()
|
||||
sso_redirect_url = f"{idp_sso_url}?{urlencode({'SAMLRequest': encoded})}"
|
||||
|
||||
return {
|
||||
"request_id": request_id,
|
||||
"authn_request_xml": authn_request,
|
||||
"encoded_request": encoded,
|
||||
"redirect_url": sso_redirect_url,
|
||||
}
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("=" * 60)
|
||||
print("Google Workspace SSO Configuration Validator")
|
||||
print("=" * 60)
|
||||
print()
|
||||
print("Usage:")
|
||||
print(" validator = GoogleWorkspaceSSOValidator('example.com')")
|
||||
print(" result = validator.validate_sso_configuration(")
|
||||
print(" idp_sso_url='https://idp.example.com/sso/saml',")
|
||||
print(" idp_entity_id='https://idp.example.com',")
|
||||
print(" cert_pem=open('idp_cert.pem').read()")
|
||||
print(" )")
|
||||
print(" print(json.dumps(result, indent=2))")
|
||||
Reference in New Issue
Block a user