Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 70 additions & 0 deletions Password-Checker/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# Password-Checker

Password-Checker is a simple script that checks the strength of a given password and provides suggestions to improve its security. It also suggests a better password based on the given password.

You can either provide your input as a command line argument or interactively through the terminal _(but it's always recommended to use interactive session on console)_. This is a basic script that perform minimum basic checks and suggestion.

> [!IMPORTANT]
> Since this script serves as a foundational example, it may not be fully suitable for real-world use cases yet. However, its purpose is to establish a solid groundwork for more advanced versions. As the author, I look forward to seeing further improvements and refactoring that will enhance this script to meet real-world requirements.

## Requirements

The only requirement for running this script in your local system is Python 3.6 or above. No external dependencies are required.

## Usage

### For *unix-based systems
To use this script, run the following command:

```bash
curl -s https://raw.githubusercontent.com/Grow-with-Open-Source/Python-Projects/main/Password-Checker/check-password.py | python
```

or

```bash
wget -qO- https://raw.githubusercontent.com/Grow-with-Open-Source/Python-Projects/main/Password-Checker/check-password.py | python
```

or you can download the file from GitHub and then run the script by giving permission to execute the file as shown below:

```bash
# Downloading the script
wget https://raw.githubusercontent.com/Grow-with-Open-Source/Python-Projects/main/Password-Checker/check-password.py

# --- OR ---
# curl -o script.py https://raw.githubusercontent.com/Grow-with-Open-Source/Python-Projects/main/Password-Checker/check-password.py
# ----------

# Giving permission to execute the file
chmod +x check-password.py

# Running the script
./check-password.py
```

### For Windows

Usually, powershell in Window 10 or later version consist of `curl` binary, so you can do that same thing as shown above. But in case it doesn't work, you can use the following command:

```powershell
Invoke-WebRequest https://raw.githubusercontent.com/Grow-with-Open-Source/Python-Projects/main/Password-Checker/check-password.py -OutFile "$env:TEMP\temp_script.py"
python "$env:TEMP\temp_script.py"
```

If you want to save the script for later usage, then it's best recommended to download the script in a desired location and run the script using python interpreter.

## Contributing

Please make sure you have used it this script before you start contributing, and then please go through the [Contributing Guidelines](https://github.com/Grow-with-Open-Source/Python-Projects/blob/main/CONTRIBUTING.md) to make your contribution.

> [!NOTE]
> Since this mini-project was meant to be a sample groundwork for more advancements, add your changes and contributions into the following [Change Log](#change-log) in the given format.

## Change Log

- PR [#37](https://github.com/Grow-with-Open-Source/Python-Projects/pull/37): Created the basic script with minimum features.

## License

This project is released under the [Apache License 2.0](https://github.com/Grow-with-Open-Source/Python-Projects/blob/main/LICENSE).
257 changes: 257 additions & 0 deletions Password-Checker/check-password.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,257 @@
#!/usr/bin/env python
import re
import sys
import random
from getpass import getpass

# ANSI escape codes for colors
COLOR = {
"RED": '\033[91m',
"YELLOW": '\033[93m',
"GREEN": '\033[92m',
"BLUE": '\033[94m',
"RESET": '\033[0m'
}

KEYBOARD_PATTERNS = ['qwerty', 'asdfgh', 'zxcvbn']
COMMON_SUBSTITUTIONS = {
'@': 'a', '4': 'a', '3': 'e', '0': 'o',
'1': 'i', '$': 's', '7': 't'
}

COMMON_WORDS = {
'adjectives': ['Happy', 'Clever', 'Swift', 'Brave', 'Bright'],
'nouns': ['Tiger', 'River', 'Mountain', 'Storm', 'Star'],
'numbers': ['365', '42', '777', '314', '999'],
'separators': ['_', '.', '#', '*', '@']
}

PATTERNS = {
'uppercase': re.compile(r'[A-Z]'),
'lowercase': re.compile(r'[a-z]'),
'numbers': re.compile(r'\d'),
'special': re.compile(r'[!@#$%^&*(),.?":{}|<>]')
}


def format_to_header(
msg: str,
*,
rep: float = 1,
new_line_at_end: bool = False,
is_main_header: bool = False):
if not isinstance(msg, str):
raise TypeError("msg must be a string")

res_str = "\n"
no_of_hypens = int(len(msg)*rep)

if is_main_header:
no_of_hypens += 4
msg = f"| {msg.upper()} |"

header_str = [
'-'*no_of_hypens,
msg,
'-'*no_of_hypens,
]

res_str += "\n".join(header_str)
if new_line_at_end:
res_str += "\n"
return res_str


def check_password_strength(password):
score = 0
suggestions = []

# Check length
if len(password) < 12:
suggestions.append("Password should be at least 12 characters long.")
elif len(password) >= 16:
score += 2
else:
score += 1

# Check for uppercase
if not PATTERNS['uppercase'].search(password):
suggestions.append("Add uppercase letters.")
else:
score += 1

# Check for lowercase
if not PATTERNS['lowercase'].search(password):
suggestions.append("Add lowercase letters.")
else:
score += 1

# Check for numbers
if not PATTERNS['numbers'].search(password):
suggestions.append("Add numbers.")
else:
score += 1

# Check for special characters
if not PATTERNS['special'].search(password):
suggestions.append("Add special characters.")
else:
score += 1

# Check for repeated patterns (like 'testtest')
half_length = len(password) // 2
for i in range(2, half_length + 1):
if password[:i] * (len(password) // i) == password[:len(password) // i * i]:
suggestions.append("Avoid repeating patterns in your password.")
score -= 1
break

# Check for keyboard patterns
lower_pass = password.lower()
for pattern in KEYBOARD_PATTERNS:
if pattern in lower_pass:
suggestions.append("Avoid common keyboard patterns")
score -= 1
break

# Check for simple character substitutions
substituted = password.lower()
for k, v in COMMON_SUBSTITUTIONS.items():
substituted = substituted.replace(k, v)
if substituted.isalpha() and len(substituted) > 3:
suggestions.append(
"Using symbol substitutions (like '@' for 'a') isn't very secure.")
score -= 1

# Ensure score doesn't go below 0
score = max(0, score)

return score, suggestions


def categorize_password(score):
if score < 2:
return "WEAK", COLOR["RED"]
if score < 4:
return "GOOD", COLOR["YELLOW"]
return "STRONG", COLOR["GREEN"]


def create_memorable_suggestion(base_word):
adj = random.choice(COMMON_WORDS['adjectives'])
noun = random.choice(COMMON_WORDS['nouns'])
num = random.choice(COMMON_WORDS['numbers'])
sep = random.choice(COMMON_WORDS['separators'])

# Use the base word if it's good enough (not too short and has letters)
if len(base_word) >= 4 and any(c.isalpha() for c in base_word):
base = base_word.capitalize()
else:
base = noun

patterns = [
f"{adj}{sep}{base}{num}",
f"{base}{sep}{noun}{num}",
f"{num}{sep}{adj}{base}"
]

return random.choice(patterns)


def suggest_better_password(password):
# If password is very weak, create a completely new memorable one
score, _ = check_password_strength(password)
if score < 2:
return create_memorable_suggestion(password)

suggestion = password

# Smart character substitutions (maintain readability)
smart_subs = {
'a': '@', 'e': '3', 'i': '!', 'o': '0', 's': '$',
'ate': '8', 'to': '2', 'for': '4'
}

# Apply substitutions intelligently
for word, replacement in smart_subs.items():
if word in suggestion.lower() and random.random() < 0.5: # 50% chance
suggestion = suggestion.replace(word, replacement)

# Ensure at least one capital letter in a natural position
if not any(c.isupper() for c in suggestion):
words = suggestion.split()
if words:
words[0] = words[0].capitalize()
suggestion = ''.join(words)

# Add complexity if needed while keeping it memorable
if len(suggestion) < 12:
suggestion += random.choice(COMMON_WORDS['numbers'])

if not re.search(r'[!@#$%^&*(),.?":{}|<>]', suggestion):
suggestion += random.choice(COMMON_WORDS['separators'])

return suggestion


def input_handler():
if len(sys.argv) > 1:
password = sys.argv[1]
print(
f"{COLOR['RED']}It is recommended to avoid entering passwords directly on the command line,{COLOR['RESET']}")
print(
f"{COLOR['RED']}as they may be visible to others and recorded in the shell history.{COLOR['RESET']}")
return password
print(
format_to_header(
"Password Strength Checker",
new_line_at_end=True,
is_main_header=True
)
)
print("For enhanced security, your input will be hidden.")
print("Hence, you may not see the characters as you type.")
try:
password = getpass("\nEnter password to check: ")
except KeyboardInterrupt:
print("\nExiting...")
sys.exit(0)
return password


def output_handler(password, category, color, suggestions):
print(f"\nPassword Strength: {color}{category}{COLOR['RESET']}")

if suggestions:
print(format_to_header("Suggestions to improve:"))
for suggestion in suggestions:
print(f"{COLOR['BLUE']}- {suggestion}{COLOR['RESET']}")

# Add this block to show suggested password
if category != "STRONG":
better_password = suggest_better_password(password)
print(
f"\nSuggested stronger password: {COLOR['GREEN']}{better_password}{COLOR['RESET']}")

points_to_remember = [
"Never use your personal information while creating a password.",
"Consider using a passphrase made up of multiple words for better security.",
"Avoid using common phrases or easily guessable patterns.",
"Avoid using the same password for multiple accounts.",
"Regularly update your passwords to enhance security.",
"Use a reputable password manager to generate and store complex passwords securely."
]
print(format_to_header('Points to Remember:'))
for points in points_to_remember:
print(f"{COLOR['BLUE']}- {points}{COLOR['RESET']}")


def main():
password = input_handler()
score, suggestions = check_password_strength(password)
category, color = categorize_password(score)
output_handler(password, category, color, suggestions)


if __name__ == "__main__":
main()
Loading