update from lal-10

This commit is contained in:
Diego Fernando Carrión
2025-11-17 15:27:21 +01:00
parent f286ec70f5
commit ce36541c14
5 changed files with 460 additions and 6 deletions

View File

@@ -66,6 +66,11 @@ then
LS_TYPE=bsd
fi
if [ -x "$(command -v aws)" ]
then
alias aws='AWS_PROFILE=$(cat ~/.aws/.profile) aws'
fi
if ! limited_terminal
then
if [ -x "$(command -v lsd)" ]

View File

@@ -58,9 +58,9 @@ prompt_cmd() {
echo -n "]"
fi
fi
if [ -x "$(which aws-creds)" ]
if [ -x "$(which aws)" ]
then
awsprofile=$(aws-creds 2>/dev/null)
awsprofile=$(cat ~/.aws/.profile)
if [ ! -z "$awsprofile" ]
then
tput setaf $BASE_COLOR

View File

@@ -2,19 +2,19 @@
"CopilotChat.nvim": { "branch": "main", "commit": "3d16702149d61d0c0b84a72756e6d2a65f3f30a8" },
"LuaSnip": { "branch": "master", "commit": "ccf25a5452b8697a823de3e5ecda63ed3d723b79" },
"NvChad": { "branch": "v2.5", "commit": "f107fabe11ac8013dc3435ecd5382bee872b1584" },
"base46": { "branch": "v3.0", "commit": "db58475d3fd2a16f9b1467d6895e3c4c195ed7dd" },
"base46": { "branch": "v2.5", "commit": "fde7a2cd54599e148d376f82980407c2d24b0fa2" },
"cmp-async-path": { "branch": "main", "commit": "0ed1492f59e730c366d261a5ad822fa37e44c325" },
"cmp-buffer": { "branch": "main", "commit": "b74fab3656eea9de20a9b8116afa3cfc4ec09657" },
"cmp-nvim-lsp": { "branch": "main", "commit": "bd5a7d6db125d4654b50eeae9f5217f24bb22fd3" },
"cmp-nvim-lua": { "branch": "main", "commit": "f12408bdb54c39c23e67cab726264c10db33ada8" },
"cmp_luasnip": { "branch": "master", "commit": "98d9cb5c2c38532bd9bdb481067b20fea8f32e90" },
"conform.nvim": { "branch": "master", "commit": "9fd3d5e0b689ec1bf400c53cbbec72c6fdf24081" },
"conform.nvim": { "branch": "master", "commit": "26c02e1155a4980900bdccabca4516f4c712aae9" },
"ctrlsf-ui.nvim": { "branch": "main", "commit": "4afee49d999946f25b84de004aeec857f094fb6c" },
"ctrlsf.vim": { "branch": "master", "commit": "50186c529c881b7844bcd66c7b8e9826eb8990a4" },
"friendly-snippets": { "branch": "main", "commit": "572f5660cf05f8cd8834e096d7b4c921ba18e175" },
"gitsigns.nvim": { "branch": "main", "commit": "20ad4419564d6e22b189f6738116b38871082332" },
"indent-blankline.nvim": { "branch": "master", "commit": "005b56001b2cb30bfa61b7986bc50657816ba4ba" },
"lazy.nvim": { "branch": "main", "commit": "f0f5bbb9e5bfae5e6468f9359ffea3d151418176" },
"lazy.nvim": { "branch": "main", "commit": "202d8e92b3a74ac88eb3a7f1e40fb59b4c2a6535" },
"lazygit.nvim": { "branch": "main", "commit": "2305deed25bc61b866d5d39189e9105a45cf1cfb" },
"luarocks.nvim": { "branch": "main", "commit": "1db9093915eb16ba2473cfb8d343ace5ee04130a" },
"mason.nvim": { "branch": "main", "commit": "ad7146aa61dcaeb54fa900144d768f040090bff0" },
@@ -34,7 +34,7 @@
"nvim-treesitter": { "branch": "master", "commit": "42fc28ba918343ebfd5565147a42a26580579482" },
"nvim-web-devicons": { "branch": "master", "commit": "8dcb311b0c92d460fac00eac706abd43d94d68af" },
"plenary.nvim": { "branch": "master", "commit": "b9fd5226c2f76c951fc8ed5923d85e4de065e509" },
"schemastore.nvim": { "branch": "main", "commit": "8d37e8eb0e48712c823f959beaecf0b3a7a01e75" },
"schemastore.nvim": { "branch": "main", "commit": "1b894a46cff15b95f62fc639ccb9081bb90abef0" },
"tagbar": { "branch": "master", "commit": "7bfffca1f121afb7a9e38747500bf5270e006bb1" },
"telescope.nvim": { "branch": "master", "commit": "b4da76be54691e854d3e0e02c36b0245f945c2c7" },
"todo-comments.nvim": { "branch": "main", "commit": "411503d3bedeff88484de572f2509c248e499b38" },

View File

@@ -0,0 +1,45 @@
#!/usr/bin/env bash
PROFILE_FILE=~/.aws/.profile
AWS_CONFIG=~/.aws/config
PROFILE_LIST_CMD="grep -P '^\[profile .+\]' $AWS_CONFIG | perl -pe 's/\[profile (\S+)\]/\1/'"
current_profile="< none >"
if [ -f $PROFILE_FILE ]
then
current_profile="$(cat $PROFILE_FILE)"
fi
deisred_profile=""
profile_list=$(eval "$PROFILE_LIST_CMD")
if [ -x "$(command -v fzf)" ]
then
deisred_profile=$(echo "$profile_list" | fzf)
else
echo "$profile_list"
echo
read -p "Which Profile to use?: " deisred_profile
fi
if [ -n "$deisred_profile" ]
then
if ! grep -qP "^${deisred_profile}$" <(echo "$profile_list")
then
echo "Invalid Profile Selected!"
exit 2
fi
else
echo "No Profile Selected."
exit 3
fi
echo
if [[ "$current_profile" == "$deisred_profile" ]]
then
echo "Profile Unchanged ($current_profile == $deisred_profile)"
else
echo "Swtiching from $current_profile --> $deisred_profile"
echo "$deisred_profile" > $PROFILE_FILE
fi

404
home/any/scripts/bin/inspect-cert Executable file
View File

@@ -0,0 +1,404 @@
#!/usr/bin/env python3
"""
SSL/TLS Certificate Inspector
Fetches and displays certificate details from HTTPS endpoints
"""
import ssl
import socket
import sys
from datetime import datetime
import argparse
from urllib.parse import urlparse
import subprocess
import tempfile
import os
def get_certificate(hostname, port=443, timeout=10):
"""
Fetch SSL certificate from a given hostname and port
Even if the certificate is invalid
"""
# Create an SSL context that doesn't verify certificates
context = ssl.create_default_context()
context.check_hostname = False
context.verify_mode = ssl.CERT_NONE
# Store verification errors if any
verification_errors = []
# Try to verify the certificate separately to report issues
try:
verify_context = ssl.create_default_context()
with socket.create_connection((hostname, port), timeout=timeout) as sock:
with verify_context.wrap_socket(sock, server_hostname=hostname) as ssock:
pass # If we get here, cert is valid
except ssl.SSLError as e:
verification_errors.append(str(e))
except Exception:
pass
# Now get the certificate without verification
with socket.create_connection((hostname, port), timeout=timeout) as sock:
with context.wrap_socket(sock, server_hostname=hostname) as ssock:
# Get certificate in binary form (always works)
der_cert = ssock.getpeercert(binary_form=True)
pem_cert = ssl.DER_cert_to_PEM_cert(der_cert)
# Get cipher info
cipher_info = ssock.cipher()
version_info = ssock.version()
# Parse the certificate using OpenSSL or manual parsing
cert_details = parse_certificate_details(pem_cert)
return cert_details, pem_cert, verification_errors, cipher_info, version_info
def parse_certificate_details(pem_cert):
"""
Parse certificate details from PEM certificate
"""
details = {}
# First try using OpenSSL command if available
try:
with tempfile.NamedTemporaryFile(mode='w', suffix='.pem', delete=False) as f:
f.write(pem_cert)
temp_path = f.name
try:
# Run openssl to get certificate text
result = subprocess.run(
['openssl', 'x509', '-in', temp_path, '-text', '-noout'],
capture_output=True,
text=True,
timeout=5
)
if result.returncode == 0:
details = parse_openssl_text(result.stdout)
else:
# Try with -inform DER if PEM didn't work
result = subprocess.run(
['openssl', 'x509', '-in', temp_path, '-inform', 'PEM', '-text', '-noout'],
capture_output=True,
text=True,
timeout=5
)
if result.returncode == 0:
details = parse_openssl_text(result.stdout)
except (subprocess.SubprocessError, FileNotFoundError):
# OpenSSL not available, try basic parsing
details = basic_parse_pem(pem_cert)
finally:
try:
os.unlink(temp_path)
except:
pass
except Exception as e:
details = basic_parse_pem(pem_cert)
return details
def parse_openssl_text(text):
"""
Parse the output of openssl x509 -text
"""
import re
details = {}
# Parse subject
subject_match = re.search(r'Subject:\s*(.+?)(?=\n\s+)', text)
if subject_match:
details['subject'] = subject_match.group(1).strip()
# Parse issuer
issuer_match = re.search(r'Issuer:\s*(.+?)(?=\n)', text)
if issuer_match:
details['issuer'] = issuer_match.group(1).strip()
# Parse serial number
serial_match = re.search(r'Serial Number:\s*\n?\s*([a-fA-F0-9:\s]+?)(?=\n)', text)
if serial_match:
serial = serial_match.group(1).strip().replace(':', '').replace(' ', '').upper()
details['serialNumber'] = serial
# Parse signature algorithm
sig_algo_match = re.search(r'Signature Algorithm:\s*(.+?)(?=\n)', text)
if sig_algo_match:
details['signatureAlgorithm'] = sig_algo_match.group(1).strip()
# Parse validity dates
not_before_match = re.search(r'Not Before:\s*(.+?)(?=\n)', text)
if not_before_match:
details['notBefore'] = not_before_match.group(1).strip()
not_after_match = re.search(r'Not After\s*:\s*(.+?)(?=\n)', text)
if not_after_match:
details['notAfter'] = not_after_match.group(1).strip()
# Parse version
version_match = re.search(r'Version:\s*(\d+)', text)
if version_match:
details['version'] = version_match.group(1)
# Parse Subject Alternative Name
san_section = re.search(r'X509v3 Subject Alternative Name:\s*(?:critical\s*)?\n\s*(.+?)(?=\n\s*[A-Z]|\n\s*$)', text)
if san_section:
san_text = san_section.group(1).strip()
# Clean up the SANs
sans = re.findall(r'(DNS|IP|email|URI):([^,\s]+)', san_text)
if sans:
details['subjectAltName'] = ', '.join([f"{t}:{v}" for t, v in sans])
# Parse Key Usage
key_usage_match = re.search(r'X509v3 Key Usage:\s*(?:critical\s*)?\n\s*(.+?)(?=\n\s*[A-Z]|\n\s*$)', text)
if key_usage_match:
details['keyUsage'] = key_usage_match.group(1).strip()
# Parse Extended Key Usage
ext_key_usage_match = re.search(r'X509v3 Extended Key Usage:\s*\n\s*(.+?)(?=\n\s*[A-Z]|\n\s*$)', text)
if ext_key_usage_match:
details['extKeyUsage'] = ext_key_usage_match.group(1).strip()
# Parse Authority Key Identifier
auth_key_match = re.search(r'X509v3 Authority Key Identifier:\s*\n\s*(.+?)(?=\n\s*[A-Z]|\n\s*$)', text)
if auth_key_match:
details['authorityKeyIdentifier'] = auth_key_match.group(1).strip().replace('\n', ' ')
# Parse Subject Key Identifier
subj_key_match = re.search(r'X509v3 Subject Key Identifier:\s*\n\s*(.+?)(?=\n\s*[A-Z]|\n\s*$)', text)
if subj_key_match:
details['subjectKeyIdentifier'] = subj_key_match.group(1).strip()
# Parse OCSP
ocsp_match = re.search(r'OCSP - URI:(.+?)(?=\n|$)', text)
if ocsp_match:
details['OCSP'] = ocsp_match.group(1).strip()
# Parse CA Issuers
ca_issuers_match = re.search(r'CA Issuers - URI:(.+?)(?=\n|$)', text)
if ca_issuers_match:
details['caIssuers'] = ca_issuers_match.group(1).strip()
# Parse CRL Distribution Points
crl_match = re.search(r'X509v3 CRL Distribution Points:\s*\n\s*(.+?)(?=\n\s*[A-Z]|\n\s*$)', text, re.DOTALL)
if crl_match:
crl_text = crl_match.group(1).strip()
crl_uris = re.findall(r'URI:(.+?)(?=\n|$)', crl_text)
if crl_uris:
details['crlDistributionPoints'] = ', '.join(crl_uris)
return details
def basic_parse_pem(pem_cert):
"""
Basic parsing when OpenSSL is not available
"""
import base64
import re
details = {}
# Extract base64 content
cert_match = re.search(r'-----BEGIN CERTIFICATE-----\s*(.+?)\s*-----END CERTIFICATE-----',
pem_cert, re.DOTALL)
if cert_match:
details['format'] = 'X.509 Certificate (PEM encoded)'
# We can't parse much without additional libraries
details['note'] = 'Install OpenSSL for detailed parsing: apt-get install openssl'
return details
def format_certificate_details(details, verification_errors, cipher_info, version_info):
"""
Format certificate details for display
"""
output = []
# Add verification status
if verification_errors:
output.append(("⚠️ Certificate Status", "INVALID/UNTRUSTED"))
for error in verification_errors:
output.append((" Verification Error", error))
else:
output.append(("✓ Certificate Status", "VALID"))
output.append(("", "")) # Empty line
# Certificate details
if 'subject' in details:
output.append(("Subject", details['subject']))
if 'issuer' in details:
output.append(("Issuer", details['issuer']))
if 'serialNumber' in details:
output.append(("Serial Number", details['serialNumber']))
if 'signatureAlgorithm' in details:
output.append(("Signature Algorithm", details['signatureAlgorithm']))
if 'version' in details:
output.append(("Version", details['version']))
# Validity
if 'notBefore' in details:
output.append(("Valid From", details['notBefore']))
if 'notAfter' in details:
output.append(("Valid Until", details['notAfter']))
# Try to calculate expiry
try:
# Parse various date formats
for fmt in ['%b %d %H:%M:%S %Y %Z', '%b %d %H:%M:%S %Y %Z']:
try:
expiry_date = datetime.strptime(details['notAfter'], fmt)
days_remaining = (expiry_date - datetime.now()).days
if days_remaining > 0:
output.append(("Days Until Expiry", f"{days_remaining} days"))
if days_remaining < 30:
output.append((" ⚠️ Warning", "Certificate expires soon!"))
else:
output.append((" ⚠️ Expiry Status", f"EXPIRED ({abs(days_remaining)} days ago)"))
break
except:
continue
except:
pass
# Extensions
if 'subjectAltName' in details:
output.append(("Subject Alt Names", details['subjectAltName']))
if 'keyUsage' in details:
output.append(("Key Usage", details['keyUsage']))
if 'extKeyUsage' in details:
output.append(("Extended Key Usage", details['extKeyUsage']))
if 'authorityKeyIdentifier' in details:
output.append(("Authority Key ID", details['authorityKeyIdentifier'][:50] + '...'
if len(details['authorityKeyIdentifier']) > 50
else details['authorityKeyIdentifier']))
if 'subjectKeyIdentifier' in details:
output.append(("Subject Key ID", details['subjectKeyIdentifier']))
# OCSP and CRL
if 'OCSP' in details:
output.append(("OCSP", details['OCSP']))
if 'caIssuers' in details:
output.append(("CA Issuers", details['caIssuers']))
if 'crlDistributionPoints' in details:
output.append(("CRL Distribution Points", details['crlDistributionPoints']))
# Note if limited parsing
if 'note' in details:
output.append(("", ""))
output.append(("Note", details['note']))
return output
def print_certificate_details(hostname, port, cert_details, verification_errors,
cipher_info, version_info, pem_cert, show_pem):
"""
Print certificate details in a formatted way
"""
print("=" * 80)
print(f"SSL/TLS Certificate Details for {hostname}:{port}")
print("=" * 80)
print()
# Connection info
if version_info:
print(f"TLS Version: {version_info}")
if cipher_info:
print(f"Cipher Suite: {cipher_info[0]} ({cipher_info[2]} bits)")
print()
# Format and print details
output = format_certificate_details(cert_details, verification_errors, cipher_info, version_info)
if output:
max_label_length = max(len(label) for label, _ in output if label)
for label, value in output:
if label:
print(f"{label:<{max_label_length + 2}}: {value}")
else:
print()
# Show PEM if requested or if parsing failed
if show_pem or not cert_details or 'note' in cert_details:
print()
print("=" * 80)
print("PEM Certificate:")
print("=" * 80)
print(pem_cert)
def parse_url_or_hostname(input_str):
"""
Parse input string to extract hostname and port
"""
port = 443
if '://' in input_str:
parsed = urlparse(input_str)
hostname = parsed.hostname
if parsed.port:
port = parsed.port
else:
if ':' in input_str:
parts = input_str.rsplit(':', 1)
hostname = parts[0]
try:
port = int(parts[1])
except ValueError:
hostname = input_str
else:
hostname = input_str
return hostname, port
def main():
parser = argparse.ArgumentParser(
description='Fetch and display SSL/TLS certificate details from HTTPS endpoints'
)
parser.add_argument('target', help='Target hostname, hostname:port, or HTTPS URL')
parser.add_argument('--show-pem', action='store_true', help='Always show the PEM certificate')
parser.add_argument('--timeout', type=int, default=10, help='Connection timeout (default: 10)')
args = parser.parse_args()
try:
hostname, port = parse_url_or_hostname(args.target)
print(f"Fetching certificate from {hostname}:{port}...")
print("Note: Certificate validation is disabled to show all certificate details")
print()
cert_details, pem_cert, verification_errors, cipher_info, version_info = get_certificate(
hostname, port, args.timeout
)
print_certificate_details(
hostname, port, cert_details, verification_errors,
cipher_info, version_info, pem_cert, args.show_pem
)
except Exception as e:
print(f"Error: {e}", file=sys.stderr)
sys.exit(1)
if __name__ == "__main__":
main()