Skip to content

Conversation

@Bhanu97282
Copy link

@Bhanu97282 Bhanu97282 commented Jul 15, 2025

Summary by CodeRabbit

  • New Features
    • Introduced a script to audit AppRole authentication configurations in HashiCorp Vault and generate an Excel report with detailed findings.
  • Documentation
    • Added a new badge to the README displaying CodeRabbit Pull Request Reviews status.

@coderabbitai
Copy link

coderabbitai bot commented Jul 15, 2025

Walkthrough

A badge for CodeRabbit Pull Request Reviews was added to the README. Additionally, a new Python script was introduced to audit HashiCorp Vault AppRole authentication across namespaces, gather TTL data, and generate a formatted Excel report. Several functions were implemented for Vault interaction, data collection, and reporting.

Changes

File(s) Change Summary
README.md Added a CodeRabbit Pull Request Reviews badge below the website screenshot.
test.py Introduced a script to audit Vault AppRole secrets/roles, collect TTLs, and export to Excel.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Script
    participant Vault
    participant ExcelWorkbook

    User->>Script: Run script
    Script->>Vault: Authenticate using token
    Script->>Vault: List namespaces under "admin"
    loop For each namespace
        Script->>Vault: List AppRole roles
        loop For each role
            Script->>Vault: Get role metadata
            Script->>Vault: List secret ID accessors
            loop For each accessor
                Script->>Vault: Get accessor TTL
                Script->>ExcelWorkbook: Record details if TTL criteria met
            end
        end
        Script->>Vault: List auth methods
        Script->>ExcelWorkbook: Record auth methods
    end
    Script->>ExcelWorkbook: Save workbook
    Script->>User: Print progress and errors
Loading

Poem

A badge now shines in README’s light,
While Vault secrets hide from sight.
A script hops through namespaces deep,
Gathering secrets AppRoles keep.
With Excel sheets and tidy rows,
The rabbit’s audit swiftly grows!
🐇📊

✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🔭 Outside diff range comments (1)
test.py (1)

151-201: Improve main function structure and error handling.

Issues identified:

  1. Hardcoded "admin" namespace should be configurable
  2. Multiple saves of the same workbook (inefficient)
  3. No error handling or cleanup
 def main():
+    root_namespace = os.getenv("VAULT_ROOT_NAMESPACE", "admin")
+    output_file = "./auth_secret_id_TTL.xlsx"
+    
     if not client.is_authenticated():
         print("Vault authentication failed.")
         return

-    workbook = openpyxl.Workbook()
+    try:
+        workbook = openpyxl.Workbook()
         # ... header setup code ...
-        workbook.save(filename="./auth_secret_id_TTL.xlsx")
         
         # ... more sheet creation ...
-        workbook.save(filename="./auth_secret_id_TTL.xlsx")
         
-        namespaces = list_namespaces("admin")
+        namespaces = list_namespaces(root_namespace)
         
         # ... processing code ...
         
+        # Save once at the end
+        workbook.save(filename=output_file)
+        print(f"Report saved to {output_file}")
+    except Exception as e:
+        print(f"Error generating report: {e}")
+        # Clean up partial file if it exists
+        if os.path.exists(output_file):
+            os.remove(output_file)
+        raise

Also consider refactoring to pass the workbook object to each function instead of repeatedly loading/saving it.

🧹 Nitpick comments (3)
test.py (3)

6-6: Remove duplicate import.

hvac.exceptions is already available through the hvac import on line 1.

-import hvac.exceptions

117-117: Fix typo in comment.

-    approle_fill = PatternFill(start_color="FEDCAA", end_color="FEDCAA", fill_type="solid") #Peach color for highling AppRole auth methods
+    approle_fill = PatternFill(start_color="FEDCAA", end_color="FEDCAA", fill_type="solid") #Peach color for highlighting AppRole auth methods

1-201: Add documentation and consider security implications.

  1. Add module and function docstrings to explain the purpose and usage
  2. Consider the security implications of writing sensitive Vault data to an unencrypted Excel file
"""
Vault AppRole Audit Script

This script audits HashiCorp Vault AppRole configurations across namespaces,
identifying roles and secret IDs with TTLs that are zero or exceed one year.

Environment Variables:
    VAULT_ADDR: Vault server address
    VAULT_TOKEN: Authentication token
    VAULT_ROOT_NAMESPACE: Starting namespace (default: admin)
"""

For security, consider:

  • Encrypting the output file
  • Restricting file permissions
  • Using a more secure output format
  • Implementing audit logging
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c6c9d11 and 19c8b65.

📒 Files selected for processing (2)
  • README.md (1 hunks)
  • test.py (1 hunks)
🔇 Additional comments (2)
README.md (1)

19-19: Badge addition looks good!

The CodeRabbit PR review status badge is properly formatted and well-placed in the README.

test.py (1)

15-31: Fix variable scope issue in exception handlers.

The all_namespaces variable is not defined in the exception handler scopes, which will cause a NameError.

 def list_namespaces(namespace=""):
     client.adapter.namespace = namespace
+    all_namespaces = []
     try:
-        all_namespaces = []
         response = client.sys.list_namespaces()
         namespaces = response.get("data", {}).get("keys", [])
         print(namespaces)
         full_namespaces = [namespace + "/" + ns.strip("/") for ns in namespaces] # Ensure no leading/trailing slashes
         for ns in full_namespaces:
             all_namespaces.append(ns)
             all_namespaces.extend(list_namespaces(ns))
         return all_namespaces
     except hvac.exceptions.InvalidPath: #If invalid, it means we are at the root or no namespaces exist
         return all_namespaces
     except hvac.exceptions.Forbidden:
         return all_namespaces

Likely an incorrect or invalid review comment.

Comment on lines +74 to +111
def check_approle_roles(namespace, rownum):
workbook = openpyxl.load_workbook("./auth_secret_id_TTL.xlsx")

sheet = workbook.active = workbook["Approle Roles"]
client.adapter.namespace = namespace #Change to namespace from parameter
current_row = rownum
rows = []
try:
roles = client.auth.approle.list_roles()["data"]["keys"]

for role in roles: #Go through each role in approle and check the secret_id_ttl settings
try:
role_id_data = client.auth.approle.read_role(role_name=role)["data"] #Grab role metadata
ttl = role_id_data["secret_id_ttl"]
if ttl == 0 or ttl > TTL_THRESHOLD: #If ttl is 0 or greater than threshold, add to spreadsheet
rows.append([namespace, role, ttl])
print(f"[!] Namespace: '{namespace or 'root'}', Role: '{role}',TTL: {ttl} seconds")
except hvac.exceptions.InvalidPath: #Skip invalid path as it means role can potentially not exist or have metadata
pass
except hvac.exceptions.InvalidPath:
pass
except Exception as e:
print(f"Error in namespace '{namespace}': {e}")

for row_index, row_data in enumerate(rows, current_row): #Input values into sheet
current_row+=1
for col_index, value in enumerate(row_data, start=1):
sheet.cell(row=row_index, column=col_index, value=value)

for column_cells in sheet.columns: #Adjust column widths based on content
new_column_length = max(len(str(cell.value)) for cell in column_cells)
new_column_letter = (chr(64+(column_cells[0].column)))
if new_column_length > 0:
sheet.column_dimensions[new_column_letter].width = new_column_length # *1.10

workbook.save(filename="./auth_secret_id_TTL.xlsx")
return current_row #Return the next row number to write to for the next function call

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Extract common functionality to reduce duplication.

This function has identical workbook handling and column width adjustment code as check_approle_secret_id.

Consider extracting common functionality:

def adjust_column_widths(sheet):
    """Adjust column widths based on content."""
    from openpyxl.utils import get_column_letter
    for column_cells in sheet.columns:
        new_column_length = max(len(str(cell.value)) for cell in column_cells)
        column_letter = get_column_letter(column_cells[0].column)
        if new_column_length > 0:
            sheet.column_dimensions[column_letter].width = new_column_length * 1.1

def write_rows_to_sheet(sheet, rows, start_row):
    """Write rows to sheet starting at start_row."""
    for i, row_data in enumerate(rows):
        row_index = start_row + i
        for col_index, value in enumerate(row_data, start=1):
            sheet.cell(row=row_index, column=col_index, value=value)
    return start_row + len(rows)
🤖 Prompt for AI Agents
In test.py from lines 74 to 111, the function check_approle_roles duplicates
workbook handling and column width adjustment code found in
check_approle_secret_id. To fix this, extract the common logic into separate
helper functions such as adjust_column_widths(sheet) to handle column resizing
and write_rows_to_sheet(sheet, rows, start_row) to write data rows starting at a
given row. Replace the duplicated code in check_approle_roles with calls to
these helper functions to reduce redundancy and improve maintainability.

Comment on lines +8 to +13
VAULT_ADDR = "https://hcp-vault-private-vault-fc507e0d.5d5b1f21.z1.hashicorp.cloud:8200"
VAULT_TOKEN = os.getenv("VAULT_TOKEN")
TTL_THRESHOLD = 31536000 # 1 year in seconds

client = hvac.Client(url=VAULT_ADDR, token=VAULT_TOKEN)
print(client.is_authenticated())
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Critical: Hardcoded Vault URL and missing error handling.

  1. The Vault URL should not be hardcoded. Use environment variables or configuration files.
  2. Add validation for the VAULT_TOKEN environment variable.
  3. Add error handling for client initialization.
-VAULT_ADDR = "https://hcp-vault-private-vault-fc507e0d.5d5b1f21.z1.hashicorp.cloud:8200"
-VAULT_TOKEN = os.getenv("VAULT_TOKEN")
+VAULT_ADDR = os.getenv("VAULT_ADDR")
+VAULT_TOKEN = os.getenv("VAULT_TOKEN")
+
+if not VAULT_ADDR:
+    raise ValueError("VAULT_ADDR environment variable is required")
+if not VAULT_TOKEN:
+    raise ValueError("VAULT_TOKEN environment variable is required")
+
 TTL_THRESHOLD = 31536000  # 1 year in seconds

-client = hvac.Client(url=VAULT_ADDR, token=VAULT_TOKEN)
-print(client.is_authenticated())
+try:
+    client = hvac.Client(url=VAULT_ADDR, token=VAULT_TOKEN)
+    if not client.is_authenticated():
+        raise ValueError("Failed to authenticate with Vault")
+except Exception as e:
+    print(f"Failed to initialize Vault client: {e}")
+    raise
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
VAULT_ADDR = "https://hcp-vault-private-vault-fc507e0d.5d5b1f21.z1.hashicorp.cloud:8200"
VAULT_TOKEN = os.getenv("VAULT_TOKEN")
TTL_THRESHOLD = 31536000 # 1 year in seconds
client = hvac.Client(url=VAULT_ADDR, token=VAULT_TOKEN)
print(client.is_authenticated())
VAULT_ADDR = os.getenv("VAULT_ADDR")
VAULT_TOKEN = os.getenv("VAULT_TOKEN")
if not VAULT_ADDR:
raise ValueError("VAULT_ADDR environment variable is required")
if not VAULT_TOKEN:
raise ValueError("VAULT_TOKEN environment variable is required")
TTL_THRESHOLD = 31536000 # 1 year in seconds
try:
client = hvac.Client(url=VAULT_ADDR, token=VAULT_TOKEN)
if not client.is_authenticated():
raise ValueError("Failed to authenticate with Vault")
except Exception as e:
print(f"Failed to initialize Vault client: {e}")
raise
🤖 Prompt for AI Agents
In test.py around lines 8 to 13, the Vault URL is hardcoded, the VAULT_TOKEN
environment variable is not validated, and there is no error handling for client
initialization. Replace the hardcoded Vault URL with a value read from an
environment variable or configuration file. Add a check to ensure VAULT_TOKEN is
set and raise an appropriate error or exit if it is missing. Wrap the
hvac.Client initialization in a try-except block to catch and handle any
exceptions, logging or printing meaningful error messages.

Comment on lines +32 to +73
def check_approle_secret_id(namespace, rownum):
workbook = openpyxl.load_workbook("./auth_secret_id_TTL.xlsx")

sheet = workbook.active = workbook["Approle Secret ID"] #Change to Approle Secret ID sheet
client.adapter.namespace = namespace #Change to namespace from parameter
current_row = rownum
rows = []
try:
roles = client.auth.approle.list_roles()["data"]["keys"]
print(roles)

for role in roles: #Iterate through each role and if secret_id_ttl is greater than threshold
print(role)
try:
secret_id_accessors = client.auth.approle.list_secret_id_accessors(role_name=role)["data"]["keys"]
for accessor in secret_id_accessors:
secret_id_info = client.auth.approle.read_secret_id_accessor(role_name=role, secret_id_accessor=accessor)
ttl = secret_id_info["data"]["secret_id_ttl"]
if ttl == 0 or ttl > TTL_THRESHOLD:
rows.append([namespace, role, accessor, ttl])
print(f"[!] Namespace: '{namespace or 'root'}', Role: '{role}', Secret ID Accessor: {accessor}, TTL: {ttl} seconds")
except hvac.exceptions.InvalidPath:
pass
except hvac.exceptions.InvalidPath:
pass
except Exception as e:
print(f"Error in namespace '{namespace}': {e}")

for row_index, row_data in enumerate(rows, current_row): #Input values into sheet
current_row+=1
for col_index, value in enumerate(row_data, start=1):
sheet.cell(row=row_index, column=col_index, value=value)

for column_cells in sheet.columns:
new_column_length = max(len(str(cell.value)) for cell in column_cells)
new_column_letter = (chr(64+(column_cells[0].column)))
if new_column_length > 0:
sheet.column_dimensions[new_column_letter].width = new_column_length # *1.10

workbook.save(filename="./auth_secret_id_TTL.xlsx")
return current_row

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Refactor to improve efficiency and fix column handling.

Multiple issues identified:

  1. Workbook is loaded and saved for each namespace (inefficient)
  2. Column width calculation only works for columns A-Z
  3. The current_row increment affects enumerate behavior

Consider passing the workbook as a parameter instead of loading/saving repeatedly. For the column width issue:

-    for column_cells in sheet.columns:
-      new_column_length = max(len(str(cell.value)) for cell in column_cells)
-      new_column_letter = (chr(64+(column_cells[0].column)))
-      if new_column_length > 0:
-        sheet.column_dimensions[new_column_letter].width = new_column_length # *1.10
+    from openpyxl.utils import get_column_letter
+    for column_cells in sheet.columns:
+        new_column_length = max(len(str(cell.value)) for cell in column_cells)
+        column_letter = get_column_letter(column_cells[0].column)
+        if new_column_length > 0:
+            sheet.column_dimensions[column_letter].width = new_column_length * 1.1

For the row tracking:

-    for row_index, row_data in enumerate(rows, current_row): #Input values into sheet
-        current_row+=1
+    for i, row_data in enumerate(rows):
+        row_index = current_row + i
         for col_index, value in enumerate(row_data, start=1):
             sheet.cell(row=row_index, column=col_index, value=value)
+    current_row += len(rows)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def check_approle_secret_id(namespace, rownum):
workbook = openpyxl.load_workbook("./auth_secret_id_TTL.xlsx")
sheet = workbook.active = workbook["Approle Secret ID"] #Change to Approle Secret ID sheet
client.adapter.namespace = namespace #Change to namespace from parameter
current_row = rownum
rows = []
try:
roles = client.auth.approle.list_roles()["data"]["keys"]
print(roles)
for role in roles: #Iterate through each role and if secret_id_ttl is greater than threshold
print(role)
try:
secret_id_accessors = client.auth.approle.list_secret_id_accessors(role_name=role)["data"]["keys"]
for accessor in secret_id_accessors:
secret_id_info = client.auth.approle.read_secret_id_accessor(role_name=role, secret_id_accessor=accessor)
ttl = secret_id_info["data"]["secret_id_ttl"]
if ttl == 0 or ttl > TTL_THRESHOLD:
rows.append([namespace, role, accessor, ttl])
print(f"[!] Namespace: '{namespace or 'root'}', Role: '{role}', Secret ID Accessor: {accessor}, TTL: {ttl} seconds")
except hvac.exceptions.InvalidPath:
pass
except hvac.exceptions.InvalidPath:
pass
except Exception as e:
print(f"Error in namespace '{namespace}': {e}")
for row_index, row_data in enumerate(rows, current_row): #Input values into sheet
current_row+=1
for col_index, value in enumerate(row_data, start=1):
sheet.cell(row=row_index, column=col_index, value=value)
for column_cells in sheet.columns:
new_column_length = max(len(str(cell.value)) for cell in column_cells)
new_column_letter = (chr(64+(column_cells[0].column)))
if new_column_length > 0:
sheet.column_dimensions[new_column_letter].width = new_column_length # *1.10
workbook.save(filename="./auth_secret_id_TTL.xlsx")
return current_row
def check_approle_secret_id(namespace, rownum):
workbook = openpyxl.load_workbook("./auth_secret_id_TTL.xlsx")
sheet = workbook.active = workbook["Approle Secret ID"] #Change to Approle Secret ID sheet
client.adapter.namespace = namespace #Change to namespace from parameter
current_row = rownum
rows = []
try:
roles = client.auth.approle.list_roles()["data"]["keys"]
print(roles)
for role in roles: #Iterate through each role and if secret_id_ttl is greater than threshold
print(role)
try:
secret_id_accessors = client.auth.approle.list_secret_id_accessors(role_name=role)["data"]["keys"]
for accessor in secret_id_accessors:
secret_id_info = client.auth.approle.read_secret_id_accessor(role_name=role, secret_id_accessor=accessor)
ttl = secret_id_info["data"]["secret_id_ttl"]
if ttl == 0 or ttl > TTL_THRESHOLD:
rows.append([namespace, role, accessor, ttl])
print(f"[!] Namespace: '{namespace or 'root'}', Role: '{role}', Secret ID Accessor: {accessor}, TTL: {ttl} seconds")
except hvac.exceptions.InvalidPath:
pass
except hvac.exceptions.InvalidPath:
pass
except Exception as e:
print(f"Error in namespace '{namespace}': {e}")
# Correct row tracking
for i, row_data in enumerate(rows):
row_index = current_row + i
for col_index, value in enumerate(row_data, start=1):
sheet.cell(row=row_index, column=col_index, value=value)
current_row += len(rows)
# Fix column width for A–ZZZ… using openpyxl utility
from openpyxl.utils import get_column_letter
for column_cells in sheet.columns:
new_column_length = max(len(str(cell.value)) for cell in column_cells)
column_letter = get_column_letter(column_cells[0].column)
if new_column_length > 0:
sheet.column_dimensions[column_letter].width = new_column_length * 1.1
workbook.save(filename="./auth_secret_id_TTL.xlsx")
return current_row
🤖 Prompt for AI Agents
In test.py from lines 32 to 73, improve efficiency by loading the workbook once
outside the function and passing it as a parameter to avoid repeated loading and
saving for each namespace. Fix the column width calculation to handle columns
beyond 'Z' by converting the column index to the correct Excel column letter(s)
instead of assuming single letters. Also, adjust the row tracking by removing
the manual increment of current_row inside the loop since enumerate already
handles row indexing; use the row_index directly for writing cells and update
current_row after processing all rows.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant