diff --git a/.github/workflows/auto-label.yml b/.github/workflows/auto-label.yml index bbd10d13..58707cd4 100644 --- a/.github/workflows/auto-label.yml +++ b/.github/workflows/auto-label.yml @@ -27,7 +27,7 @@ jobs: owner: context.repo.owner, repo: context.repo.repo, issue_number: item.number, - labels: ['gssoc-ext', 'hacktoberfest-accepted'] + labels: ['gssoc-ext'] }); const addLabel = async (label) => { await github.rest.issues.addLabels({ diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 00000000..f3c3575f --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,47 @@ +# Security Policy + +## Reporting a Vulnerability + +We take the security of our project seriously. If you discover a security vulnerability, please follow these steps: + +1. **DO NOT** create a public GitHub issue for the vulnerability. +2. Send a report to our team through our Discord server: https://discord. +3. Provide as much information as possible about the vulnerability: + - Type of issue + - Full paths of source file(s) related to the issue + - Location of the affected source code + - Any special configuration required to reproduce the issue + - Step-by-step instructions to reproduce the issue + - Proof-of-concept or exploit code (if possible) + - Impact of the issue + +## Response Timeline + +- We will acknowledge receipt of your vulnerability report within 48 hours. +- We will provide a more detailed response within 7 days. +- We will work on fixing the vulnerability and will keep you informed of our progress. +- Once the vulnerability is fixed, we will publicly disclose the security issue. + +## Supported Versions + +We will address security vulnerabilities in the following versions: + +| Version | Supported | +| ------- | ------------------ | +| latest | :white_check_mark: | + +## Best Practices + +- Please give us reasonable time to address the issue before making any public disclosure. +- Act in good faith towards our users' privacy and data. +- Do not access or modify other users' data without explicit permission. + +## Recognition + +We appreciate the security research community's efforts in helping keep our project safe. Responsible disclosure of vulnerabilities helps us ensure the security and privacy of our users. + +## Contact + +For any security-related concerns, please contact us through: +- Discord: https://discord. +Thank you for helping keep our community safe! \ No newline at end of file diff --git a/Tests/Cipher_algorithms/test_affine_cipher.py b/Tests/Cipher_algorithms/test_affine_cipher.py new file mode 100644 index 00000000..e6d72463 --- /dev/null +++ b/Tests/Cipher_algorithms/test_affine_cipher.py @@ -0,0 +1,17 @@ +import unittest +from pysnippets.Cipher_algorithms.affine_cipher import AffineCipher + +class TestAffineCipher(unittest.TestCase): + def setUp(self): + self.cipher = AffineCipher(a=5, b=8) + + def test_encrypt(self): + self.assertEqual(self.cipher.encrypt('HELLO'), 'MJQQT') + self.assertEqual(self.cipher.encrypt('AFFINE CIPHER'), 'IHHWVC SWFRCP') + + def test_decrypt(self): + self.assertEqual(self.cipher.decrypt('MJQQT'), 'HELLO') + self.assertEqual(self.cipher.decrypt('IHHWVC SWFRCP'), 'AFFINE CIPHER') + +if __name__ == '__main__': + unittest.main() diff --git a/Tests/Cipher_algorithms/test_caesar_cipher.py b/Tests/Cipher_algorithms/test_caesar_cipher.py new file mode 100644 index 00000000..9fcfbc7e --- /dev/null +++ b/Tests/Cipher_algorithms/test_caesar_cipher.py @@ -0,0 +1,74 @@ +import unittest +import sys +import os + +sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '../../..'))) +from pysnippets.Cipher_algorithms.caesar_cipher import CaesarCipher + +class TestCaesarCipher(unittest.TestCase): + def setUp(self): + self.cipher = CaesarCipher() + + def test_default_encryption(self): + message = "Hello, World!" + expected = "Khoor, Zruog!" + self.assertEqual(self.cipher.encrypt(message), expected) + + def test_default_decryption(self): + encrypted = "Khoor, Zruog!" + expected = "Hello, World!" + self.assertEqual(self.cipher.decrypt(encrypted), expected) + + def test_custom_shift(self): + custom_cipher = CaesarCipher(shift=5) + message = "Python Programming" + encrypted = custom_cipher.encrypt(message) + decrypted = custom_cipher.decrypt(encrypted) + self.assertEqual(decrypted, message) + + def test_non_alphabetic_characters(self): + message = "Hello, World! 123" + encrypted = self.cipher.encrypt(message) + decrypted = self.cipher.decrypt(encrypted) + self.assertEqual(message, decrypted) + + def test_brute_force_decryption(self): + message = "Hello, World!" + encrypted = self.cipher.encrypt(message) + decryptions = self.cipher.brute_force_decrypt(encrypted) + found_decryption = any( + decryption == message for _, decryption in decryptions + ) + self.assertTrue(found_decryption) + + def test_full_cycle(self): + message = "The quick brown fox jumps over the lazy dog!" + encrypted = self.cipher.encrypt(message) + decrypted = self.cipher.decrypt(encrypted) + self.assertEqual(message, decrypted) + + def test_empty_string(self): + message = "" + encrypted = self.cipher.encrypt(message) + decrypted = self.cipher.decrypt(encrypted) + self.assertEqual(message, decrypted) + + def test_large_shift(self): + custom_cipher = CaesarCipher(shift=30) + message = "Large Shift" + encrypted = custom_cipher.encrypt(message) + decrypted = custom_cipher.decrypt(encrypted) + self.assertEqual(decrypted, message) + + def test_negative_shift(self): + custom_cipher = CaesarCipher(shift=-3) + message = "Negative Shift" + encrypted = custom_cipher.encrypt(message) + decrypted = custom_cipher.decrypt(encrypted) + self.assertEqual(decrypted, message) + + def test_non_string_input(self): + with self.assertRaises(TypeError): + self.cipher.encrypt(12345) + with self.assertRaises(TypeError): + self.cipher.decrypt(12345) diff --git a/Tests/Cipher_algorithms/test_playfair_cipher.py b/Tests/Cipher_algorithms/test_playfair_cipher.py new file mode 100644 index 00000000..323f06a3 --- /dev/null +++ b/Tests/Cipher_algorithms/test_playfair_cipher.py @@ -0,0 +1,17 @@ +import unittest +from pysnippets.Cipher_algorithms.playfair_cipher import playfair_encrypt, playfair_decrypt + +class TestPlayfairCipher(unittest.TestCase): + + def test_playfair_encrypt(self): + self.assertEqual(playfair_encrypt("HELLO", "KEYWORD"), "RIJVS") + self.assertEqual(playfair_encrypt("PLAYFAIR", "KEYWORD"), "RLBMZIXR") + self.assertEqual(playfair_encrypt("HACKTOBERFEST", "KEYWORD"), "RIBKZQKZBFXZ") + + def test_playfair_decrypt(self): + self.assertEqual(playfair_decrypt("RIJVS", "KEYWORD"), "HELXLO") + self.assertEqual(playfair_decrypt("RLBMZIXR", "KEYWORD"), "PLAYFAIR") + self.assertEqual(playfair_decrypt("RIBKZQKZBFXZ", "KEYWORD"), "HACKTOBERFEST") + +if __name__ == '__main__': + unittest.main() diff --git a/pysnippets/Cipher_algorithms/affine_cipher.py b/pysnippets/Cipher_algorithms/affine_cipher.py new file mode 100644 index 00000000..6f34cbdb --- /dev/null +++ b/pysnippets/Cipher_algorithms/affine_cipher.py @@ -0,0 +1,18 @@ +class AffineCipher: + def __init__(self, a, b): + self.a = a + self.b = b + self.m = 26 # Length of the alphabet + + def encrypt(self, plaintext): + return ''.join( + chr(((self.a * (ord(char) - ord('A')) + self.b) % self.m) + ord('A')) + if char.isalpha() else char for char in plaintext.upper() + ) + + def decrypt(self, ciphertext): + a_inv = pow(self.a, -1, self.m) + return ''.join( + chr(((a_inv * ((ord(char) - ord('A')) - self.b)) % self.m) + ord('A')) + if char.isalpha() else char for char in ciphertext.upper() + ) diff --git a/pysnippets/Cipher_algorithms/caesar_cipher.py b/pysnippets/Cipher_algorithms/caesar_cipher.py new file mode 100644 index 00000000..fb023178 --- /dev/null +++ b/pysnippets/Cipher_algorithms/caesar_cipher.py @@ -0,0 +1,49 @@ +import string + +class CaesarCipher: + def __init__(self, shift=3): + self.shift = shift % 26 + self.encrypt_table = self._create_translation_table(self.shift) + self.decrypt_table = self._create_translation_table(-self.shift) + + def _create_translation_table(self, shift): + lower = string.ascii_lowercase + upper = string.ascii_uppercase + lower_shifted = lower[shift:] + lower[:shift] + upper_shifted = upper[shift:] + upper[:shift] + trans_table = str.maketrans( + lower + upper, + lower_shifted + upper_shifted + ) + return trans_table + + def encrypt(self, message): + if not isinstance(message, str): + raise TypeError("Message must be a string") + return message.translate(self.encrypt_table) + + def decrypt(self, encrypted_message): + if not isinstance(encrypted_message, str): + raise TypeError("Encrypted message must be a string") + return encrypted_message.translate(self.decrypt_table) + + def brute_force_decrypt(self, encrypted_message): + possible_decryptions = [] + for potential_shift in range(26): + temp_cipher = CaesarCipher(shift=potential_shift) + decrypted = temp_cipher.decrypt(encrypted_message) + possible_decryptions.append((potential_shift, decrypted)) + return possible_decryptions + +def main(): + cipher = CaesarCipher() + original_message = "Hello, World!" + print("Original Message:", original_message) + encrypted_message = cipher.encrypt(original_message) + print("Encrypted Message:", encrypted_message) + decrypted_message = cipher.decrypt(encrypted_message) + print("Decrypted Message:", decrypted_message) + print("\nBrute Force Decryption:") + possible_decryptions = cipher.brute_force_decrypt(encrypted_message) + for shift, decryption in possible_decryptions: + print(f"Shift {shift}: {decryption}") \ No newline at end of file diff --git a/pysnippets/Cipher_algorithms/playfair_cipher.py b/pysnippets/Cipher_algorithms/playfair_cipher.py new file mode 100644 index 00000000..6e3cec4b --- /dev/null +++ b/pysnippets/Cipher_algorithms/playfair_cipher.py @@ -0,0 +1,61 @@ +def generate_key_table(key): + key = ''.join(sorted(set(key), key=lambda x: key.index(x))) + key += ''.join(chr(i) for i in range(65, 91) if chr(i) not in key and chr(i) != 'J') + return [list(key[i:i+5]) for i in range(0, 25, 5)] + +def preprocess_text(text): + text = text.upper().replace('J', 'I') + processed_text = "" + i = 0 + while i < len(text): + processed_text += text[i] + if i + 1 < len(text) and text[i] == text[i + 1]: + processed_text += 'X' + elif i + 1 < len(text): + processed_text += text[i + 1] + i += 1 + i += 1 + if len(processed_text) % 2 != 0: + processed_text += 'X' + return processed_text + +def find_position(char, key_table): + for i, row in enumerate(key_table): + if char in row: + return i, row.index(char) + return None + +def playfair_encrypt(plaintext, key): + key_table = generate_key_table(key) + plaintext = preprocess_text(plaintext) + ciphertext = "" + for i in range(0, len(plaintext), 2): + row1, col1 = find_position(plaintext[i], key_table) + row2, col2 = find_position(plaintext[i + 1], key_table) + if row1 == row2: + ciphertext += key_table[row1][(col1 + 1) % 5] + ciphertext += key_table[row2][(col2 + 1) % 5] + elif col1 == col2: + ciphertext += key_table[(row1 + 1) % 5][col1] + ciphertext += key_table[(row2 + 1) % 5][col2] + else: + ciphertext += key_table[row1][col2] + ciphertext += key_table[row2][col1] + return ciphertext + +def playfair_decrypt(ciphertext, key): + key_table = generate_key_table(key) + plaintext = "" + for i in range(0, len(ciphertext), 2): + row1, col1 = find_position(ciphertext[i], key_table) + row2, col2 = find_position(ciphertext[i + 1], key_table) + if row1 == row2: + plaintext += key_table[row1][(col1 - 1) % 5] + plaintext += key_table[row2][(col2 - 1) % 5] + elif col1 == col2: + plaintext += key_table[(row1 - 1) % 5][col1] + plaintext += key_table[(row2 - 1) % 5][col2] + else: + plaintext += key_table[row1][col2] + plaintext += key_table[row2][col1] + return plaintext diff --git a/pysnippets/Communication/send_email.py b/pysnippets/Communication/send_email.py index 88f7ff9d..db218995 100644 --- a/pysnippets/Communication/send_email.py +++ b/pysnippets/Communication/send_email.py @@ -1,50 +1,44 @@ import smtplib +import os from email.mime.text import MIMEText from email.mime.multipart import MIMEMultipart +from typing import Optional def send_email( - sender_email, - sender_password, - recepient_email, - subject, - body, - smtp_server="smtp.gmail.com", - smtp_port=587, -): + sender_email: str, + sender_password: str, + recipient_email: str, + subject: str, + body: str, + content_type: str = "plain", + smtp_server: str = "smtp.gmail.com", + smtp_port: int = 587, +) -> bool: """ - Sends an email using the provided SMTP server. + Sends an email to the specified recipient. Args: - sender_email (str): The email address of the sender. + sender_email (str): The sender's email address. sender_password (str): The password for the sender's email account. - recepient_mail (str): The email address of the recepient. - subject (str): The subject of the email. - body (str): The body of the email. - smtp_server (str, optional): The SMTP server to use. Defaults to 'smtp.gmail.com'. + recipient_email (str): The recipient's email address. + subject (str): The subject line of the email. + body (str): The content of the email. + content_type (str, optional): The format of the email body, either 'plain' or 'html'. Defaults to 'plain'. + smtp_server (str, optional): The SMTP server to connect to. Defaults to 'smtp.gmail.com'. smtp_port (int, optional): The port to use for the SMTP server. Defaults to 587. - Raises: - smtplib.SMTPAuthenticationError: If authentication fails. - smtplib.SMTPException: If there's an error sending the email. - - Example: - send_email( - "aashishnkumar@gmail.com", - "your_password", - "aashishnandakumar.official.in@gmail.com", - "Test Subject", - "This is a test email", - ) + Returns: + bool: True if the email is sent successfully, False otherwise. """ # create the email message message = MIMEMultipart() message["From"] = sender_email - message["To"] = recepient_email + message["To"] = recipient_email message["Subject"] = subject # attach the body of the email - message.attach(MIMEText(body, "plain")) + message.attach(MIMEText(body, content_type)) try: # create a secure SSL/TLS connection @@ -55,22 +49,35 @@ def send_email( # send the email server.send_message(message) print("Email sent successfully!") + return True except smtplib.SMTPAuthenticationError: - print( - "SMTP Authentication Error: The server didn't accept the username/password combination." - ) - raise + print("SMTP Authentication Error: Please check your credentials.") + except smtplib.SMTPConnectError: + print("SMTP Connection Error: Unable to connect to the SMTP server.") + except smtplib.SMTPServerDisconnected: + print("SMTP Disconnection Error: The server unexpectedly disconnected.") except smtplib.SMTPException as e: - print(f"SMTP Error: An error occured while sending the email: {str(e)}") - raise + print(f"SMTP Error: An error occurred while sending the email: {str(e)}") + except Exception as e: + print(f"Network Error: A network-related error occurred: {str(e)}") + + return False if __name__ == "__main__": - # NOTE: Never hardcode sensitive information like this in practice - send_email( - "sender@gmail.com", - "password", - "receiver@gmail.com", - "Test Subject", - "This is a test Email", - ) + # Load credentials from environment variables for security + sender_email = os.getenv("SENDER_EMAIL") + sender_password = os.getenv("SENDER_PASSWORD") + + # Ensure credentials are provided + if not sender_email or not sender_password: + print("Error: Please set the SENDER_EMAIL and SENDER_PASSWORD environment variables.") + else: + send_email( + sender_email=sender_email, + sender_password=sender_password, + recipient_email="receiver@gmail.com", + subject="Test Subject", + body="

This is a test email

This email contains HTML formatting.

", + content_type="html" + ) \ No newline at end of file