Back to skill

Security audit

Secure Password Tool

Security checks for vulnerabilities and agentic risk

Overview

This is a small offline password generator with no network access or persistence, though it lacks bounds checks for password length and count.

Before installing, be aware that the tool is offline and narrowly scoped, but you should avoid using zero, negative, or extremely large length/count values. Generated secrets are printed to the terminal, so handle terminal history, logs, and screen visibility accordingly.

Vulnerability Patterns
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (1)

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/password_generator.py:43
Finding

Unvalidated Password Length and Count Allow Empty Passwords and Resource Exhaustion

Content
View full analysis

Vulnerability Details

File Location: scripts/password_generator.py, lines 43 and 59-83
Vulnerability Type: Missing input validation
Risk Level: Medium

Vulnerable Code

python
return ''.join(secrets.choice(chars) for _ in range(length))
python
parser.add_argument('length', type=int, nargs='?', default=16, help='Password length (default: 16)')
parser.add_argument('count', type=int, nargs='?', default=1, help='Number of passwords (default: 1)')
parser.add_argument('--no-upper', action='store_true', help='Exclude uppercase letters')
parser.add_argument('--no-lower', action='store_true', help='Exclude lowercase letters')
parser.add_argument('--no-digits', action='store_true', help='Exclude digits')
parser.add_argument('--no-special', action='store_true', help='Exclude special symbols')
parser.add_argument('--exclude-similar', action='store_true', help='Exclude similar characters (0O1lI)')
parser.add_argument('--json', action='store_true', help='Output as JSON')

args = parser.parse_args()

# Generate passwords
passwords = [
    generate_password(
        args.length,
        use_upper=not args.no_upper,
        use_lower=not args.no_lower,
        use_digits=not args.no_digits,
        use_special=not args.no_special,
        exclude_similar=args.exclude_similar
    )
    for _ in range(args.count)
]

Technical Analysis

The positional length and count arguments are parsed as unrestricted integers. No lower or upper bounds are enforced before these values control password generation and list construction.

A zero or negative length causes range(length) to produce no iterations, so generate_password() silently returns an empty string. This violates the tool's security purpose and may cause an empty value to be accepted downstream as a generated credential.

Very large positive length or count values cause repeated cryptographic random selection ...[truncated 1451 chars]

Remediation
View remediation

Remediation Suggestions

  • Validate both positional arguments before generation.
  • Require a security-appropriate minimum password length, such as 12 or 16 characters.
  • Define practical maximum values for password length and batch count based on expected use.
  • Reject zero and negative values with a clear command-line error.
  • Apply the same checks inside generate_password() so direct library callers cannot bypass command-line validation.
  • Avoid constructing an unbounded list; generate and emit passwords incrementally where possible.
  • Add tests covering zero, negative, boundary, and excessively large inputs.

Example validation helper:

python
def bounded_int(minimum, maximum):
    def parse(value):
        parsed = int(value)
        if not minimum <= parsed <= maximum:
            raise argparse.ArgumentTypeError(
                f"value must be between {minimum} and {maximum}"
            )
        return parsed
    return parse

parser.add_argument(
    'length',
    type=bounded_int(12, 1024),
    nargs='?',
    default=16,
    help='Password length (12-1024; default: 16)'
)
parser.add_argument(
    'count',
    type=bounded_int(1, 1000),
    nargs='?',
    default=1,
    help='Number of passwords (1-1000; default: 1)'
)

The exact upper bounds should be selected according to legitimate operational requirements.

Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep