gophish-ruby 0.4.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/docs/EXAMPLES.md CHANGED
@@ -8,6 +8,8 @@ This document contains practical examples for common use cases with the Gophish
8
8
  - [CSV Operations](#csv-operations)
9
9
  - [Template Operations](#template-operations)
10
10
  - [Page Operations](#page-operations)
11
+ - [SMTP Operations](#smtp-operations)
12
+ - [Campaign Operations](#campaign-operations)
11
13
  - [Error Handling](#error-handling)
12
14
  - [Advanced Scenarios](#advanced-scenarios)
13
15
  - [Production Examples](#production-examples)
@@ -1236,6 +1238,1639 @@ pages = create_page_variants("Microsoft Security Alert", base_html, variants)
1236
1238
  puts "\nCreated #{pages.length} page variants for A/B testing"
1237
1239
  ```
1238
1240
 
1241
+ ## SMTP Operations
1242
+
1243
+ ### Basic SMTP Profile Creation
1244
+
1245
+ ```ruby
1246
+ # Simple SMTP profile without authentication
1247
+ basic_smtp = Gophish::Smtp.new(
1248
+ name: "Company Mail Server",
1249
+ host: "smtp.company.com",
1250
+ from_address: "security@company.com"
1251
+ )
1252
+
1253
+ if basic_smtp.save
1254
+ puts "✓ Basic SMTP profile created: #{basic_smtp.id}"
1255
+ puts " Host: #{basic_smtp.host}"
1256
+ puts " From: #{basic_smtp.from_address}"
1257
+ else
1258
+ puts "✗ Failed to create SMTP profile: #{basic_smtp.errors.full_messages}"
1259
+ end
1260
+ ```
1261
+
1262
+ ### SMTP Profile with Authentication
1263
+
1264
+ ```ruby
1265
+ # SMTP with username/password authentication
1266
+ auth_smtp = Gophish::Smtp.new(
1267
+ name: "Gmail SMTP with Authentication",
1268
+ host: "smtp.gmail.com",
1269
+ from_address: "phishing.test@company.com",
1270
+ username: "smtp_service@company.com",
1271
+ password: "app_specific_password",
1272
+ ignore_cert_errors: false
1273
+ )
1274
+
1275
+ puts "SMTP Configuration:"
1276
+ puts " Name: #{auth_smtp.name}"
1277
+ puts " Host: #{auth_smtp.host}"
1278
+ puts " From: #{auth_smtp.from_address}"
1279
+ puts " Uses Authentication: #{auth_smtp.has_authentication?}"
1280
+ puts " Ignores SSL Errors: #{auth_smtp.ignores_cert_errors?}"
1281
+
1282
+ if auth_smtp.save
1283
+ puts "✓ Authenticated SMTP profile created: #{auth_smtp.id}"
1284
+ end
1285
+ ```
1286
+
1287
+ ### SMTP Profile with Custom Headers
1288
+
1289
+ ```ruby
1290
+ # SMTP profile with multiple custom headers
1291
+ header_smtp = Gophish::Smtp.new(
1292
+ name: "Custom Headers Mail Server",
1293
+ host: "mail.company.com",
1294
+ from_address: "security-team@company.com"
1295
+ )
1296
+
1297
+ # Add headers for better deliverability and tracking
1298
+ header_smtp.add_header("X-Mailer", "Gophish Security Training Platform v2.0")
1299
+ header_smtp.add_header("X-Department", "Information Security")
1300
+ header_smtp.add_header("X-Campaign-ID", "Q4-2024-PHISH-001")
1301
+ header_smtp.add_header("Return-Path", "bounces+security@company.com")
1302
+ header_smtp.add_header("Reply-To", "security-team@company.com")
1303
+ header_smtp.add_header("X-Priority", "1")
1304
+ header_smtp.add_header("X-MSMail-Priority", "High")
1305
+
1306
+ puts "SMTP Profile with Headers:"
1307
+ puts " Name: #{header_smtp.name}"
1308
+ puts " Headers: #{header_smtp.header_count}"
1309
+ puts " Has Headers: #{header_smtp.has_headers?}"
1310
+
1311
+ header_smtp.headers.each_with_index do |header, index|
1312
+ key = header[:key] || header['key']
1313
+ value = header[:value] || header['value']
1314
+ puts " #{index + 1}. #{key}: #{value}"
1315
+ end
1316
+
1317
+ if header_smtp.save
1318
+ puts "✓ SMTP profile with headers created: #{header_smtp.id}"
1319
+ end
1320
+ ```
1321
+
1322
+ ### Production-Ready SMTP Configuration
1323
+
1324
+ ```ruby
1325
+ # Comprehensive SMTP setup for production use
1326
+ def create_production_smtp(name, host, from_address, username = nil, password = nil)
1327
+ puts "Creating production SMTP profile: #{name}"
1328
+
1329
+ smtp = Gophish::Smtp.new(
1330
+ name: name,
1331
+ host: host,
1332
+ from_address: from_address,
1333
+ username: username,
1334
+ password: password,
1335
+ ignore_cert_errors: false, # Always verify SSL in production
1336
+ interface_type: "SMTP"
1337
+ )
1338
+
1339
+ # Add production-grade headers
1340
+ smtp.add_header("X-Mailer", "Corporate Security Training System")
1341
+ smtp.add_header("X-Environment", "Production")
1342
+ smtp.add_header("X-Security-Classification", "Internal")
1343
+ smtp.add_header("Return-Path", "bounces+security@#{extract_domain(from_address)}")
1344
+ smtp.add_header("List-Unsubscribe", "<mailto:security-opt-out@#{extract_domain(from_address)}>")
1345
+
1346
+ # Validate configuration
1347
+ unless smtp.valid?
1348
+ puts " ✗ Configuration invalid:"
1349
+ smtp.errors.full_messages.each { |error| puts " - #{error}" }
1350
+ return nil
1351
+ end
1352
+
1353
+ # Security checks
1354
+ puts " Security Assessment:"
1355
+ puts " SSL Verification: #{smtp.ignore_cert_errors? ? '✗ DISABLED' : '✓ ENABLED'}"
1356
+ puts " Authentication: #{smtp.has_authentication? ? '✓ ENABLED' : '⚠ DISABLED'}"
1357
+ puts " Custom Headers: #{smtp.header_count}"
1358
+ puts " From Domain: #{extract_domain(smtp.from_address)}"
1359
+
1360
+ if smtp.save
1361
+ puts " ✓ Production SMTP profile created (ID: #{smtp.id})"
1362
+ return smtp
1363
+ else
1364
+ puts " ✗ Failed to save SMTP profile:"
1365
+ smtp.errors.full_messages.each { |error| puts " - #{error}" }
1366
+ return nil
1367
+ end
1368
+ end
1369
+
1370
+ def extract_domain(email)
1371
+ email.split('@').last
1372
+ end
1373
+
1374
+ # Usage examples
1375
+ prod_smtp = create_production_smtp(
1376
+ "Production Mail Server",
1377
+ "smtp.company.com",
1378
+ "security-training@company.com",
1379
+ "smtp_service_account",
1380
+ ENV['SMTP_PASSWORD'] # Use environment variables for passwords
1381
+ )
1382
+
1383
+ gmail_smtp = create_production_smtp(
1384
+ "Gmail SMTP for Testing",
1385
+ "smtp.gmail.com",
1386
+ "test-campaigns@company.com",
1387
+ "test-account@company.com",
1388
+ ENV['GMAIL_APP_PASSWORD']
1389
+ )
1390
+ ```
1391
+
1392
+ ### SMTP Profile Management Operations
1393
+
1394
+ ```ruby
1395
+ # List all SMTP profiles with details
1396
+ def list_smtp_profiles
1397
+ smtp_profiles = Gophish::Smtp.all
1398
+ puts "Found #{smtp_profiles.length} SMTP profiles:"
1399
+
1400
+ smtp_profiles.each do |smtp|
1401
+ features = []
1402
+ features << "🔐 Auth" if smtp.has_authentication?
1403
+ features << "📧 Headers(#{smtp.header_count})" if smtp.has_headers?
1404
+ features << "⚠️ No SSL" if smtp.ignores_cert_errors?
1405
+
1406
+ feature_text = features.any? ? " [#{features.join(', ')}]" : ""
1407
+
1408
+ puts " #{smtp.id}: #{smtp.name}#{feature_text}"
1409
+ puts " Host: #{smtp.host}"
1410
+ puts " From: #{smtp.from_address}"
1411
+ puts " Interface: #{smtp.interface_type}"
1412
+ puts " Modified: #{smtp.modified_date}" if smtp.modified_date
1413
+ puts
1414
+ end
1415
+ end
1416
+
1417
+ # Update existing SMTP profile
1418
+ def update_smtp_profile(smtp_id, updates = {})
1419
+ begin
1420
+ smtp = Gophish::Smtp.find(smtp_id)
1421
+ rescue StandardError
1422
+ puts "✗ SMTP profile #{smtp_id} not found"
1423
+ return false
1424
+ end
1425
+
1426
+ puts "Updating SMTP profile '#{smtp.name}'"
1427
+ original_values = {}
1428
+
1429
+ # Apply updates and track changes
1430
+ updates.each do |field, value|
1431
+ if smtp.respond_to?("#{field}=")
1432
+ original_values[field] = smtp.send(field)
1433
+ smtp.send("#{field}=", value)
1434
+ puts " #{field}: '#{original_values[field]}' → '#{value}'"
1435
+ else
1436
+ puts " ⚠️ Unknown field: #{field}"
1437
+ end
1438
+ end
1439
+
1440
+ if smtp.save
1441
+ puts " ✓ SMTP profile updated successfully"
1442
+ true
1443
+ else
1444
+ puts " ✗ Update failed:"
1445
+ smtp.errors.full_messages.each { |error| puts " - #{error}" }
1446
+ false
1447
+ end
1448
+ end
1449
+
1450
+ # Clone SMTP profile with modifications
1451
+ def clone_smtp_profile(original_id, new_name, modifications = {})
1452
+ begin
1453
+ original = Gophish::Smtp.find(original_id)
1454
+ rescue StandardError
1455
+ puts "✗ Original SMTP profile #{original_id} not found"
1456
+ return nil
1457
+ end
1458
+
1459
+ puts "Cloning SMTP profile '#{original.name}' as '#{new_name}'"
1460
+
1461
+ # Create clone with same settings
1462
+ cloned_smtp = Gophish::Smtp.new(
1463
+ name: new_name,
1464
+ host: original.host,
1465
+ from_address: original.from_address,
1466
+ username: original.username,
1467
+ password: original.password,
1468
+ interface_type: original.interface_type,
1469
+ ignore_cert_errors: original.ignore_cert_errors
1470
+ )
1471
+
1472
+ # Copy headers
1473
+ if original.has_headers?
1474
+ original.headers.each do |header|
1475
+ key = header[:key] || header['key']
1476
+ value = header[:value] || header['value']
1477
+ cloned_smtp.add_header(key, value)
1478
+ end
1479
+ end
1480
+
1481
+ # Apply modifications
1482
+ modifications.each do |field, value|
1483
+ case field
1484
+ when :headers
1485
+ # Clear existing headers and add new ones
1486
+ cloned_smtp.headers.clear
1487
+ value.each { |key, val| cloned_smtp.add_header(key, val) }
1488
+ else
1489
+ cloned_smtp.send("#{field}=", value) if cloned_smtp.respond_to?("#{field}=")
1490
+ end
1491
+ end
1492
+
1493
+ if cloned_smtp.save
1494
+ puts " ✓ SMTP profile cloned successfully (ID: #{cloned_smtp.id})"
1495
+ puts " Features:"
1496
+ puts " Authentication: #{cloned_smtp.has_authentication?}"
1497
+ puts " Headers: #{cloned_smtp.header_count}"
1498
+ puts " SSL Verification: #{cloned_smtp.ignore_cert_errors? ? 'Disabled' : 'Enabled'}"
1499
+ return cloned_smtp
1500
+ else
1501
+ puts " ✗ Clone failed:"
1502
+ cloned_smtp.errors.full_messages.each { |error| puts " - #{error}" }
1503
+ return nil
1504
+ end
1505
+ end
1506
+
1507
+ # Usage examples
1508
+ list_smtp_profiles
1509
+
1510
+ # Update an existing profile
1511
+ update_smtp_profile(1, {
1512
+ name: "Updated Company SMTP",
1513
+ ignore_cert_errors: true
1514
+ })
1515
+
1516
+ # Clone with modifications
1517
+ cloned = clone_smtp_profile(1, "Test Environment SMTP", {
1518
+ from_address: "test@company.com",
1519
+ ignore_cert_errors: true,
1520
+ headers: {
1521
+ "X-Environment" => "Testing",
1522
+ "X-Test-Campaign" => "true"
1523
+ }
1524
+ })
1525
+ ```
1526
+
1527
+ ### Header Management Examples
1528
+
1529
+ ```ruby
1530
+ # Advanced header management
1531
+ def manage_smtp_headers(smtp_id)
1532
+ begin
1533
+ smtp = Gophish::Smtp.find(smtp_id)
1534
+ rescue StandardError
1535
+ puts "✗ SMTP profile #{smtp_id} not found"
1536
+ return
1537
+ end
1538
+
1539
+ puts "Managing headers for '#{smtp.name}'"
1540
+ puts "Current headers: #{smtp.header_count}"
1541
+
1542
+ # Display current headers
1543
+ if smtp.has_headers?
1544
+ puts " Current headers:"
1545
+ smtp.headers.each_with_index do |header, index|
1546
+ key = header[:key] || header['key']
1547
+ value = header[:value] || header['value']
1548
+ puts " #{index + 1}. #{key}: #{value}"
1549
+ end
1550
+ end
1551
+
1552
+ # Add standard deliverability headers
1553
+ standard_headers = {
1554
+ "X-Mailer" => "Gophish Security Training v4.0",
1555
+ "X-Campaign-Type" => "Security Awareness",
1556
+ "X-Department" => "IT Security",
1557
+ "Return-Path" => "bounces@#{smtp.from_address.split('@').last}",
1558
+ "List-Unsubscribe" => "<mailto:unsubscribe@#{smtp.from_address.split('@').last}>",
1559
+ "X-Priority" => "Normal",
1560
+ "X-Auto-Response-Suppress" => "All"
1561
+ }
1562
+
1563
+ puts "\nAdding standard headers:"
1564
+ standard_headers.each do |key, value|
1565
+ smtp.add_header(key, value)
1566
+ puts " + #{key}: #{value}"
1567
+ end
1568
+
1569
+ # Remove any problematic headers
1570
+ problematic_headers = ["X-Spam", "X-Test", "X-Debug"]
1571
+ removed_count = 0
1572
+
1573
+ problematic_headers.each do |header_key|
1574
+ if smtp.headers.any? { |h| (h[:key] || h['key']) == header_key }
1575
+ smtp.remove_header(header_key)
1576
+ removed_count += 1
1577
+ puts " - Removed: #{header_key}"
1578
+ end
1579
+ end
1580
+
1581
+ puts "\nHeader management summary:"
1582
+ puts " Total headers: #{smtp.header_count}"
1583
+ puts " Added: #{standard_headers.length}"
1584
+ puts " Removed: #{removed_count}"
1585
+
1586
+ if smtp.save
1587
+ puts " ✓ Headers updated successfully"
1588
+ else
1589
+ puts " ✗ Failed to save header changes"
1590
+ end
1591
+ end
1592
+
1593
+ # Bulk header operations
1594
+ def standardize_all_smtp_headers
1595
+ smtp_profiles = Gophish::Smtp.all
1596
+ puts "Standardizing headers for #{smtp_profiles.length} SMTP profiles"
1597
+
1598
+ standard_headers = {
1599
+ "X-Mailer" => "Corporate Security Training Platform",
1600
+ "X-Campaign-Source" => "Internal Security Team",
1601
+ "Return-Path" => nil, # Will be set per profile
1602
+ "List-Unsubscribe" => nil # Will be set per profile
1603
+ }
1604
+
1605
+ smtp_profiles.each_with_index do |smtp, index|
1606
+ puts "[#{index + 1}/#{smtp_profiles.length}] Processing '#{smtp.name}'"
1607
+
1608
+ # Set dynamic headers based on from_address
1609
+ domain = smtp.from_address.split('@').last
1610
+ standard_headers["Return-Path"] = "bounces@#{domain}"
1611
+ standard_headers["List-Unsubscribe"] = "<mailto:unsubscribe@#{domain}>"
1612
+
1613
+ # Add missing standard headers
1614
+ added_count = 0
1615
+ standard_headers.each do |key, value|
1616
+ unless smtp.headers.any? { |h| (h[:key] || h['key']) == key }
1617
+ smtp.add_header(key, value)
1618
+ added_count += 1
1619
+ end
1620
+ end
1621
+
1622
+ if added_count > 0
1623
+ if smtp.save
1624
+ puts " ✓ Added #{added_count} standard headers"
1625
+ else
1626
+ puts " ✗ Failed to save headers"
1627
+ end
1628
+ else
1629
+ puts " - Already has standard headers"
1630
+ end
1631
+ end
1632
+ end
1633
+
1634
+ # Usage
1635
+ manage_smtp_headers(1)
1636
+ standardize_all_smtp_headers
1637
+ ```
1638
+
1639
+ ### SMTP Configuration Templates
1640
+
1641
+ ```ruby
1642
+ # Pre-configured SMTP templates for common providers
1643
+ class SMTPTemplates
1644
+ TEMPLATES = {
1645
+ gmail: {
1646
+ host: "smtp.gmail.com",
1647
+ port: 587,
1648
+ interface_type: "SMTP",
1649
+ ignore_cert_errors: false,
1650
+ headers: {
1651
+ "X-Mailer" => "Gmail SMTP Integration",
1652
+ "X-Provider" => "Gmail"
1653
+ }
1654
+ },
1655
+
1656
+ office365: {
1657
+ host: "smtp.office365.com",
1658
+ port: 587,
1659
+ interface_type: "SMTP",
1660
+ ignore_cert_errors: false,
1661
+ headers: {
1662
+ "X-Mailer" => "Office 365 SMTP Integration",
1663
+ "X-Provider" => "Microsoft Office 365"
1664
+ }
1665
+ },
1666
+
1667
+ sendgrid: {
1668
+ host: "smtp.sendgrid.net",
1669
+ port: 587,
1670
+ interface_type: "SMTP",
1671
+ ignore_cert_errors: false,
1672
+ headers: {
1673
+ "X-Mailer" => "SendGrid SMTP Integration",
1674
+ "X-Provider" => "SendGrid"
1675
+ }
1676
+ },
1677
+
1678
+ mailgun: {
1679
+ host: "smtp.mailgun.org",
1680
+ port: 587,
1681
+ interface_type: "SMTP",
1682
+ ignore_cert_errors: false,
1683
+ headers: {
1684
+ "X-Mailer" => "Mailgun SMTP Integration",
1685
+ "X-Provider" => "Mailgun"
1686
+ }
1687
+ },
1688
+
1689
+ ses: {
1690
+ host: "email-smtp.us-east-1.amazonaws.com",
1691
+ port: 587,
1692
+ interface_type: "SMTP",
1693
+ ignore_cert_errors: false,
1694
+ headers: {
1695
+ "X-Mailer" => "Amazon SES SMTP Integration",
1696
+ "X-Provider" => "Amazon SES"
1697
+ }
1698
+ }
1699
+ }.freeze
1700
+
1701
+ def self.create_from_template(template_name, name, from_address, username, password)
1702
+ template = TEMPLATES[template_name.to_sym]
1703
+ unless template
1704
+ puts "✗ Unknown template: #{template_name}"
1705
+ puts "Available templates: #{TEMPLATES.keys.join(', ')}"
1706
+ return nil
1707
+ end
1708
+
1709
+ puts "Creating SMTP profile from #{template_name} template"
1710
+
1711
+ smtp = Gophish::Smtp.new(
1712
+ name: name,
1713
+ host: template[:host],
1714
+ from_address: from_address,
1715
+ username: username,
1716
+ password: password,
1717
+ interface_type: template[:interface_type],
1718
+ ignore_cert_errors: template[:ignore_cert_errors]
1719
+ )
1720
+
1721
+ # Add template headers
1722
+ template[:headers].each do |key, value|
1723
+ smtp.add_header(key, value)
1724
+ end
1725
+
1726
+ # Add common headers
1727
+ smtp.add_header("Return-Path", "bounces@#{from_address.split('@').last}")
1728
+ smtp.add_header("List-Unsubscribe", "<mailto:unsubscribe@#{from_address.split('@').last}>")
1729
+
1730
+ puts "Template configuration:"
1731
+ puts " Host: #{smtp.host}"
1732
+ puts " From: #{smtp.from_address}"
1733
+ puts " Headers: #{smtp.header_count}"
1734
+ puts " SSL Verification: #{smtp.ignore_cert_errors? ? 'Disabled' : 'Enabled'}"
1735
+
1736
+ if smtp.valid?
1737
+ if smtp.save
1738
+ puts " ✓ SMTP profile created successfully (ID: #{smtp.id})"
1739
+ return smtp
1740
+ else
1741
+ puts " ✗ Save failed: #{smtp.errors.full_messages.join(', ')}"
1742
+ end
1743
+ else
1744
+ puts " ✗ Validation failed: #{smtp.errors.full_messages.join(', ')}"
1745
+ end
1746
+
1747
+ nil
1748
+ end
1749
+
1750
+ def self.list_templates
1751
+ puts "Available SMTP templates:"
1752
+ TEMPLATES.each do |name, config|
1753
+ puts " #{name}:"
1754
+ puts " Host: #{config[:host]}"
1755
+ puts " Provider: #{config[:headers]['X-Provider']}"
1756
+ puts " SSL: #{config[:ignore_cert_errors] ? 'Optional' : 'Required'}"
1757
+ end
1758
+ end
1759
+ end
1760
+
1761
+ # Usage examples
1762
+ SMTPTemplates.list_templates
1763
+
1764
+ # Create Gmail SMTP
1765
+ gmail = SMTPTemplates.create_from_template(
1766
+ :gmail,
1767
+ "Corporate Gmail SMTP",
1768
+ "security@company.com",
1769
+ "security@company.com",
1770
+ ENV['GMAIL_APP_PASSWORD']
1771
+ )
1772
+
1773
+ # Create Office 365 SMTP
1774
+ office365 = SMTPTemplates.create_from_template(
1775
+ :office365,
1776
+ "Office 365 Mail Server",
1777
+ "training@company.com",
1778
+ "smtp_service@company.onmicrosoft.com",
1779
+ ENV['O365_PASSWORD']
1780
+ )
1781
+
1782
+ # Create SendGrid SMTP
1783
+ sendgrid = SMTPTemplates.create_from_template(
1784
+ :sendgrid,
1785
+ "SendGrid Transactional Mail",
1786
+ "no-reply@company.com",
1787
+ "apikey",
1788
+ ENV['SENDGRID_API_KEY']
1789
+ )
1790
+ ```
1791
+
1792
+ ### SMTP Testing and Validation
1793
+
1794
+ ```ruby
1795
+ # Comprehensive SMTP testing
1796
+ class SMTPTester
1797
+ def self.validate_smtp_profile(smtp_id)
1798
+ begin
1799
+ smtp = Gophish::Smtp.find(smtp_id)
1800
+ rescue StandardError
1801
+ puts "✗ SMTP profile #{smtp_id} not found"
1802
+ return false
1803
+ end
1804
+
1805
+ puts "Validating SMTP profile: #{smtp.name}"
1806
+ puts "=" * 50
1807
+
1808
+ # Basic validation
1809
+ unless smtp.valid?
1810
+ puts "✗ Basic validation failed:"
1811
+ smtp.errors.full_messages.each { |error| puts " - #{error}" }
1812
+ return false
1813
+ end
1814
+ puts "✓ Basic validation passed"
1815
+
1816
+ # Configuration check
1817
+ puts "\nConfiguration Details:"
1818
+ puts " Name: #{smtp.name}"
1819
+ puts " Host: #{smtp.host}"
1820
+ puts " From Address: #{smtp.from_address}"
1821
+ puts " Interface Type: #{smtp.interface_type}"
1822
+ puts " Username: #{smtp.username || 'Not configured'}"
1823
+ puts " Password: #{smtp.password ? '[SET]' : '[NOT SET]'}"
1824
+ puts " SSL Verification: #{smtp.ignore_cert_errors? ? 'Disabled' : 'Enabled'}"
1825
+
1826
+ # Authentication check
1827
+ if smtp.has_authentication?
1828
+ puts "✓ Authentication configured"
1829
+ else
1830
+ puts "⚠ No authentication configured - ensure your SMTP server allows it"
1831
+ end
1832
+
1833
+ # SSL/Security check
1834
+ if smtp.ignore_cert_errors?
1835
+ puts "⚠ SSL certificate verification disabled"
1836
+ puts " This may be acceptable for development but not for production"
1837
+ else
1838
+ puts "✓ SSL certificate verification enabled"
1839
+ end
1840
+
1841
+ # Header analysis
1842
+ puts "\nHeader Analysis:"
1843
+ if smtp.has_headers?
1844
+ puts " Custom headers: #{smtp.header_count}"
1845
+
1846
+ required_headers = ['Return-Path', 'List-Unsubscribe']
1847
+ recommended_headers = ['X-Mailer', 'X-Campaign-Type', 'Reply-To']
1848
+
1849
+ required_headers.each do |header|
1850
+ has_header = smtp.headers.any? { |h| (h[:key] || h['key']) == header }
1851
+ puts " #{has_header ? '✓' : '✗'} #{header}: #{has_header ? 'Present' : 'Missing'}"
1852
+ end
1853
+
1854
+ recommended_headers.each do |header|
1855
+ has_header = smtp.headers.any? { |h| (h[:key] || h['key']) == header }
1856
+ puts " #{has_header ? '✓' : '⚠'} #{header}: #{has_header ? 'Present' : 'Recommended'}"
1857
+ end
1858
+
1859
+ # List all headers
1860
+ puts "\n All headers:"
1861
+ smtp.headers.each_with_index do |header, index|
1862
+ key = header[:key] || header['key']
1863
+ value = header[:value] || header['value']
1864
+ puts " #{index + 1}. #{key}: #{value}"
1865
+ end
1866
+ else
1867
+ puts " ⚠ No custom headers configured"
1868
+ puts " Consider adding headers for better deliverability"
1869
+ end
1870
+
1871
+ # Domain analysis
1872
+ domain = smtp.from_address.split('@').last
1873
+ puts "\nDomain Analysis:"
1874
+ puts " From domain: #{domain}"
1875
+ puts " SMTP host: #{smtp.host}"
1876
+
1877
+ # Check if domain matches SMTP host
1878
+ if smtp.host.include?(domain) || domain.include?(smtp.host.split('.').last(2).join('.'))
1879
+ puts "✓ Domain and SMTP host appear to match"
1880
+ else
1881
+ puts "⚠ Domain and SMTP host don't obviously match"
1882
+ puts " Ensure your SMTP provider is authorized to send for #{domain}"
1883
+ end
1884
+
1885
+ puts "\n" + "=" * 50
1886
+ puts "✓ SMTP profile validation completed"
1887
+ true
1888
+ end
1889
+
1890
+ def self.security_audit_all_smtp
1891
+ smtp_profiles = Gophish::Smtp.all
1892
+ puts "Security Audit: #{smtp_profiles.length} SMTP Profiles"
1893
+ puts "=" * 60
1894
+
1895
+ issues = []
1896
+
1897
+ smtp_profiles.each_with_index do |smtp, index|
1898
+ puts "\n[#{index + 1}/#{smtp_profiles.length}] #{smtp.name}"
1899
+
1900
+ # Check for insecure settings
1901
+ if smtp.ignore_cert_errors?
1902
+ issues << "#{smtp.name}: SSL verification disabled"
1903
+ puts " ⚠️ SSL certificate verification disabled"
1904
+ end
1905
+
1906
+ unless smtp.has_authentication?
1907
+ issues << "#{smtp.name}: No authentication configured"
1908
+ puts " ⚠️ No authentication configured"
1909
+ end
1910
+
1911
+ # Check for test/debug indicators
1912
+ test_indicators = ['test', 'debug', 'dev', 'staging']
1913
+ if test_indicators.any? { |indicator| smtp.name.downcase.include?(indicator) }
1914
+ puts " ℹ️ Appears to be a test/development profile"
1915
+ end
1916
+
1917
+ # Check headers for security info
1918
+ if smtp.has_headers?
1919
+ smtp.headers.each do |header|
1920
+ key = header[:key] || header['key']
1921
+ value = header[:value] || header['value']
1922
+
1923
+ if key.downcase.include?('test') || value.downcase.include?('test')
1924
+ puts " ℹ️ Contains test-related headers"
1925
+ end
1926
+ end
1927
+ end
1928
+
1929
+ puts " ✓ Basic security check completed"
1930
+ end
1931
+
1932
+ puts "\n" + "=" * 60
1933
+ puts "Security Audit Summary:"
1934
+ if issues.any?
1935
+ puts "Issues found:"
1936
+ issues.each { |issue| puts " - #{issue}" }
1937
+ else
1938
+ puts "✓ No security issues detected"
1939
+ end
1940
+ end
1941
+ end
1942
+
1943
+ # Usage
1944
+ SMTPTester.validate_smtp_profile(1)
1945
+ SMTPTester.security_audit_all_smtp
1946
+ ```
1947
+
1948
+ ## Campaign Operations
1949
+
1950
+ ### Basic Campaign Creation
1951
+
1952
+ ```ruby
1953
+ # Create a simple campaign using existing components
1954
+ campaign = Gophish::Campaign.new(
1955
+ name: "Q1 Security Awareness Campaign",
1956
+ template: { name: "Security Awareness Training" }, # Reference by name
1957
+ page: { name: "Microsoft Office 365 Login Clone" }, # Reference by name
1958
+ groups: [{ name: "Engineering Team" }], # Reference by name
1959
+ smtp: { name: "Company Mail Server" }, # Reference by name
1960
+ url: "https://training.company.com"
1961
+ )
1962
+
1963
+ if campaign.save
1964
+ puts "✓ Campaign created successfully!"
1965
+ puts " ID: #{campaign.id}"
1966
+ puts " Name: #{campaign.name}"
1967
+ puts " Status: #{campaign.status}"
1968
+ else
1969
+ puts "✗ Failed to create campaign:"
1970
+ campaign.errors.full_messages.each { |msg| puts " - #{msg}" }
1971
+ end
1972
+ ```
1973
+
1974
+ ### Campaign Creation with Object References
1975
+
1976
+ ```ruby
1977
+ # Create campaign using actual object instances
1978
+ template = Gophish::Template.find(1)
1979
+ page = Gophish::Page.find(2)
1980
+ group = Gophish::Group.find(3)
1981
+ smtp = Gophish::Smtp.find(4)
1982
+
1983
+ campaign = Gophish::Campaign.new(
1984
+ name: "Advanced Security Training",
1985
+ template: template, # Full template object
1986
+ page: page, # Full page object
1987
+ groups: [group], # Array of group objects
1988
+ smtp: smtp, # Full SMTP object
1989
+ url: "https://secure-training.company.com"
1990
+ )
1991
+
1992
+ if campaign.save
1993
+ puts "✓ Campaign created with object references"
1994
+ puts " Template: #{campaign.template.name}"
1995
+ puts " Page: #{campaign.page.name}"
1996
+ puts " Groups: #{campaign.groups.map(&:name).join(', ')}"
1997
+ end
1998
+ ```
1999
+
2000
+ ### Scheduled Campaign Creation
2001
+
2002
+ ```ruby
2003
+ # Create a campaign with specific launch timing
2004
+ scheduled_campaign = Gophish::Campaign.new(
2005
+ name: "Monday Morning Phishing Test",
2006
+ template: { name: "IT Security Alert" },
2007
+ page: { name: "Corporate Portal Login" },
2008
+ groups: [
2009
+ { name: "Sales Team" },
2010
+ { name: "Marketing Department" }
2011
+ ],
2012
+ smtp: { name: "Gmail SMTP with Authentication" },
2013
+ url: "https://training-portal.company.com",
2014
+ launch_date: (Time.now + 2.days).beginning_of_day.iso8601, # Launch in 2 days at midnight
2015
+ send_by_date: (Time.now + 2.days).noon.iso8601 # Complete by noon
2016
+ )
2017
+
2018
+ if scheduled_campaign.save
2019
+ puts "✓ Scheduled campaign created"
2020
+ puts " Launch: #{scheduled_campaign.launch_date}"
2021
+ puts " Send by: #{scheduled_campaign.send_by_date}"
2022
+ puts " Launched? #{scheduled_campaign.launched?}"
2023
+ puts " Has deadline? #{scheduled_campaign.has_send_by_date?}"
2024
+ end
2025
+ ```
2026
+
2027
+ ### Campaign Monitoring and Analysis
2028
+
2029
+ ```ruby
2030
+ # Monitor campaign progress
2031
+ def monitor_campaign(campaign_id)
2032
+ begin
2033
+ campaign = Gophish::Campaign.find(campaign_id)
2034
+ rescue StandardError => e
2035
+ puts "✗ Campaign not found: #{e.message}"
2036
+ return
2037
+ end
2038
+
2039
+ puts "Campaign Status Report"
2040
+ puts "=" * 50
2041
+ puts "Name: #{campaign.name}"
2042
+ puts "Status: #{campaign.status}"
2043
+ puts "Created: #{campaign.created_date}"
2044
+ puts "Launched: #{campaign.launch_date || 'Not launched'}"
2045
+ puts "Completed: #{campaign.completed_date || 'Not completed'}"
2046
+ puts
2047
+
2048
+ # Status checks
2049
+ puts "Status Checks:"
2050
+ puts " In progress? #{campaign.in_progress?}"
2051
+ puts " Completed? #{campaign.completed?}"
2052
+ puts " Has launch date? #{campaign.launched?}"
2053
+ puts " Has deadline? #{campaign.has_send_by_date?}"
2054
+ puts
2055
+
2056
+ # Results analysis
2057
+ if campaign.results.any?
2058
+ puts "Results Summary:"
2059
+ puts " Total targets: #{campaign.results.length}"
2060
+
2061
+ # Count by status
2062
+ status_counts = Hash.new(0)
2063
+ campaign.results.each { |result| status_counts[result.status] += 1 }
2064
+
2065
+ status_counts.each do |status, count|
2066
+ percentage = (count.to_f / campaign.results.length * 100).round(1)
2067
+ puts " #{status}: #{count} (#{percentage}%)"
2068
+ end
2069
+
2070
+ # Behavior analysis
2071
+ clicked_count = campaign.results.count(&:clicked?)
2072
+ opened_count = campaign.results.count(&:opened?)
2073
+ reported_count = campaign.results.count(&:reported?)
2074
+ submitted_count = campaign.results.count(&:submitted_data?)
2075
+
2076
+ puts "\nBehavior Analysis:"
2077
+ puts " 📧 Opened emails: #{opened_count}"
2078
+ puts " 🔗 Clicked links: #{clicked_count}"
2079
+ puts " 📝 Submitted data: #{submitted_count}"
2080
+ puts " 🚨 Reported phishing: #{reported_count}"
2081
+ puts " 📊 Click rate: #{(clicked_count.to_f / campaign.results.length * 100).round(1)}%"
2082
+ puts " 🛡️ Report rate: #{(reported_count.to_f / campaign.results.length * 100).round(1)}%"
2083
+
2084
+ # Individual results
2085
+ if campaign.results.length <= 10
2086
+ puts "\nIndividual Results:"
2087
+ campaign.results.each do |result|
2088
+ icon = result.clicked? ? "🔗" : result.opened? ? "📧" : result.reported? ? "🚨" : "📬"
2089
+ puts " #{icon} #{result.email} - #{result.status}"
2090
+ end
2091
+ end
2092
+ else
2093
+ puts "No results available yet"
2094
+ end
2095
+
2096
+ # Timeline events
2097
+ if campaign.timeline.any?
2098
+ puts "\nRecent Timeline Events (last 5):"
2099
+ campaign.timeline.last(5).each do |event|
2100
+ puts " #{event.time}: #{event.message}"
2101
+ puts " Email: #{event.email}" if event.email
2102
+ end
2103
+ end
2104
+ end
2105
+
2106
+ # Usage
2107
+ monitor_campaign(1)
2108
+ ```
2109
+
2110
+ ### Campaign Management Operations
2111
+
2112
+ ```ruby
2113
+ # List all campaigns with status
2114
+ def list_all_campaigns
2115
+ campaigns = Gophish::Campaign.all
2116
+ puts "Found #{campaigns.length} campaigns:"
2117
+ puts
2118
+
2119
+ campaigns.each do |campaign|
2120
+ status_icon = case campaign.status
2121
+ when "In progress" then "🔄"
2122
+ when "Completed" then "✅"
2123
+ else "⏸️"
2124
+ end
2125
+
2126
+ puts "#{status_icon} #{campaign.id}: #{campaign.name}"
2127
+ puts " Status: #{campaign.status}"
2128
+ puts " Created: #{campaign.created_date}"
2129
+ puts " Launched: #{campaign.launch_date || 'Not scheduled'}"
2130
+
2131
+ if campaign.results.any?
2132
+ total = campaign.results.length
2133
+ clicked = campaign.results.count(&:clicked?)
2134
+ puts " Results: #{clicked}/#{total} clicked (#{(clicked.to_f/total*100).round(1)}%)"
2135
+ end
2136
+ puts
2137
+ end
2138
+ end
2139
+
2140
+ # Complete a running campaign
2141
+ def complete_campaign(campaign_id)
2142
+ begin
2143
+ campaign = Gophish::Campaign.find(campaign_id)
2144
+ rescue StandardError => e
2145
+ puts "✗ Campaign not found: #{e.message}"
2146
+ return false
2147
+ end
2148
+
2149
+ unless campaign.in_progress?
2150
+ puts "✗ Campaign '#{campaign.name}' is not in progress (status: #{campaign.status})"
2151
+ return false
2152
+ end
2153
+
2154
+ puts "Completing campaign '#{campaign.name}'..."
2155
+
2156
+ begin
2157
+ result = campaign.complete!
2158
+
2159
+ if result['success']
2160
+ puts "✓ Campaign completed successfully"
2161
+ puts " New status: #{campaign.status}"
2162
+ puts " Completed date: #{campaign.completed_date}"
2163
+ return true
2164
+ else
2165
+ puts "✗ Failed to complete campaign: #{result['message'] || 'Unknown error'}"
2166
+ return false
2167
+ end
2168
+ rescue StandardError => e
2169
+ puts "✗ Error completing campaign: #{e.message}"
2170
+ return false
2171
+ end
2172
+ end
2173
+
2174
+ # Clone campaign with modifications
2175
+ def clone_campaign(original_id, new_name, modifications = {})
2176
+ begin
2177
+ original = Gophish::Campaign.find(original_id)
2178
+ rescue StandardError => e
2179
+ puts "✗ Original campaign not found: #{e.message}"
2180
+ return nil
2181
+ end
2182
+
2183
+ puts "Cloning campaign '#{original.name}' as '#{new_name}'"
2184
+
2185
+ # Create clone with same settings
2186
+ cloned_campaign = Gophish::Campaign.new(
2187
+ name: new_name,
2188
+ template: original.template,
2189
+ page: original.page,
2190
+ groups: original.groups,
2191
+ smtp: original.smtp,
2192
+ url: original.url,
2193
+ launch_date: original.launch_date,
2194
+ send_by_date: original.send_by_date
2195
+ )
2196
+
2197
+ # Apply modifications
2198
+ modifications.each do |field, value|
2199
+ if cloned_campaign.respond_to?("#{field}=")
2200
+ cloned_campaign.send("#{field}=", value)
2201
+ puts " Modified #{field}: #{value}"
2202
+ else
2203
+ puts " ⚠️ Unknown field: #{field}"
2204
+ end
2205
+ end
2206
+
2207
+ if cloned_campaign.save
2208
+ puts "✓ Campaign cloned successfully (ID: #{cloned_campaign.id})"
2209
+ return cloned_campaign
2210
+ else
2211
+ puts "✗ Clone failed:"
2212
+ cloned_campaign.errors.full_messages.each { |msg| puts " - #{msg}" }
2213
+ return nil
2214
+ end
2215
+ end
2216
+
2217
+ # Usage examples
2218
+ list_all_campaigns
2219
+ complete_campaign(1)
2220
+
2221
+ # Clone with new launch date
2222
+ cloned = clone_campaign(1, "Cloned Security Training", {
2223
+ launch_date: (Time.now + 1.week).iso8601,
2224
+ url: "https://testing.company.com"
2225
+ })
2226
+ ```
2227
+
2228
+ ### Complete Campaign Workflow
2229
+
2230
+ ```ruby
2231
+ # End-to-end campaign creation and management
2232
+ class CampaignManager
2233
+ def initialize
2234
+ @logger = Logger.new(STDOUT)
2235
+ end
2236
+
2237
+ def create_complete_campaign(config)
2238
+ @logger.info "Creating complete campaign: #{config[:name]}"
2239
+
2240
+ # Step 1: Create target group
2241
+ group = create_or_find_group(config[:group_name], config[:csv_file])
2242
+ return nil unless group
2243
+
2244
+ # Step 2: Create template
2245
+ template = create_template(config[:template])
2246
+ return nil unless template
2247
+
2248
+ # Step 3: Create landing page
2249
+ page = create_page(config[:page])
2250
+ return nil unless page
2251
+
2252
+ # Step 4: Create SMTP profile
2253
+ smtp = create_or_find_smtp(config[:smtp])
2254
+ return nil unless smtp
2255
+
2256
+ # Step 5: Create campaign
2257
+ campaign = create_campaign(config, template, page, group, smtp)
2258
+ return nil unless campaign
2259
+
2260
+ @logger.info "Campaign creation completed successfully"
2261
+ campaign
2262
+ end
2263
+
2264
+ private
2265
+
2266
+ def create_or_find_group(name, csv_file)
2267
+ @logger.info "Creating group: #{name}"
2268
+
2269
+ # Check if group already exists
2270
+ existing_groups = Gophish::Group.all
2271
+ existing = existing_groups.find { |g| g.name == name }
2272
+
2273
+ if existing
2274
+ @logger.info "Using existing group: #{name} (#{existing.targets.length} targets)"
2275
+ return existing
2276
+ end
2277
+
2278
+ # Create new group
2279
+ group = Gophish::Group.new(name: name)
2280
+
2281
+ if csv_file && File.exist?(csv_file)
2282
+ csv_content = File.read(csv_file)
2283
+ group.import_csv(csv_content)
2284
+ @logger.info "Imported #{group.targets.length} targets from #{csv_file}"
2285
+ else
2286
+ @logger.warn "No CSV file provided or file not found: #{csv_file}"
2287
+ return nil
2288
+ end
2289
+
2290
+ if group.save
2291
+ @logger.info "Group created successfully (ID: #{group.id})"
2292
+ return group
2293
+ else
2294
+ @logger.error "Failed to create group: #{group.errors.full_messages.join(', ')}"
2295
+ return nil
2296
+ end
2297
+ end
2298
+
2299
+ def create_template(config)
2300
+ @logger.info "Creating template: #{config[:name]}"
2301
+
2302
+ template = Gophish::Template.new(
2303
+ name: config[:name],
2304
+ envelope_sender: config[:envelope_sender],
2305
+ subject: config[:subject],
2306
+ html: config[:html],
2307
+ text: config[:text]
2308
+ )
2309
+
2310
+ # Add attachments if specified
2311
+ if config[:attachments]
2312
+ config[:attachments].each do |attachment_config|
2313
+ file_content = File.read(attachment_config[:file_path])
2314
+ template.add_attachment(
2315
+ file_content,
2316
+ attachment_config[:content_type],
2317
+ attachment_config[:filename]
2318
+ )
2319
+ @logger.info "Added attachment: #{attachment_config[:filename]}"
2320
+ end
2321
+ end
2322
+
2323
+ if template.save
2324
+ @logger.info "Template created successfully (ID: #{template.id})"
2325
+ return template
2326
+ else
2327
+ @logger.error "Failed to create template: #{template.errors.full_messages.join(', ')}"
2328
+ return nil
2329
+ end
2330
+ end
2331
+
2332
+ def create_page(config)
2333
+ @logger.info "Creating page: #{config[:name]}"
2334
+
2335
+ page = Gophish::Page.new(
2336
+ name: config[:name],
2337
+ html: config[:html],
2338
+ capture_credentials: config[:capture_credentials] || false,
2339
+ capture_passwords: config[:capture_passwords] || false,
2340
+ redirect_url: config[:redirect_url]
2341
+ )
2342
+
2343
+ if page.save
2344
+ @logger.info "Page created successfully (ID: #{page.id})"
2345
+ return page
2346
+ else
2347
+ @logger.error "Failed to create page: #{page.errors.full_messages.join(', ')}"
2348
+ return nil
2349
+ end
2350
+ end
2351
+
2352
+ def create_or_find_smtp(config)
2353
+ @logger.info "Creating SMTP profile: #{config[:name]}"
2354
+
2355
+ # Check if SMTP profile already exists
2356
+ existing_smtps = Gophish::Smtp.all
2357
+ existing = existing_smtps.find { |s| s.name == config[:name] }
2358
+
2359
+ if existing
2360
+ @logger.info "Using existing SMTP profile: #{config[:name]}"
2361
+ return existing
2362
+ end
2363
+
2364
+ smtp = Gophish::Smtp.new(
2365
+ name: config[:name],
2366
+ host: config[:host],
2367
+ from_address: config[:from_address],
2368
+ username: config[:username],
2369
+ password: config[:password],
2370
+ ignore_cert_errors: config[:ignore_cert_errors] || false
2371
+ )
2372
+
2373
+ # Add headers if specified
2374
+ if config[:headers]
2375
+ config[:headers].each do |key, value|
2376
+ smtp.add_header(key, value)
2377
+ end
2378
+ @logger.info "Added #{config[:headers].length} custom headers"
2379
+ end
2380
+
2381
+ if smtp.save
2382
+ @logger.info "SMTP profile created successfully (ID: #{smtp.id})"
2383
+ return smtp
2384
+ else
2385
+ @logger.error "Failed to create SMTP profile: #{smtp.errors.full_messages.join(', ')}"
2386
+ return nil
2387
+ end
2388
+ end
2389
+
2390
+ def create_campaign(config, template, page, group, smtp)
2391
+ @logger.info "Creating campaign: #{config[:name]}"
2392
+
2393
+ campaign = Gophish::Campaign.new(
2394
+ name: config[:name],
2395
+ template: template,
2396
+ page: page,
2397
+ groups: [group],
2398
+ smtp: smtp,
2399
+ url: config[:url],
2400
+ launch_date: config[:launch_date],
2401
+ send_by_date: config[:send_by_date]
2402
+ )
2403
+
2404
+ if campaign.save
2405
+ @logger.info "Campaign created successfully (ID: #{campaign.id})"
2406
+ @logger.info "Campaign components:"
2407
+ @logger.info " Template: #{template.name} (ID: #{template.id})"
2408
+ @logger.info " Page: #{page.name} (ID: #{page.id})"
2409
+ @logger.info " Group: #{group.name} (#{group.targets.length} targets)"
2410
+ @logger.info " SMTP: #{smtp.name} (#{smtp.host})"
2411
+ return campaign
2412
+ else
2413
+ @logger.error "Failed to create campaign: #{campaign.errors.full_messages.join(', ')}"
2414
+ return nil
2415
+ end
2416
+ end
2417
+ end
2418
+
2419
+ # Usage example with complete configuration
2420
+ manager = CampaignManager.new
2421
+
2422
+ campaign_config = {
2423
+ name: "Q2 2024 Security Awareness Campaign",
2424
+ group_name: "All Employees Q2",
2425
+ csv_file: "employees_q2.csv",
2426
+ template: {
2427
+ name: "IT Security Alert - Q2 2024",
2428
+ envelope_sender: "noreply@company.com",
2429
+ subject: "URGENT: Security Update Required",
2430
+ html: <<~HTML,
2431
+ <html>
2432
+ <body style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto;">
2433
+ <div style="background: #f8f9fa; padding: 20px; border-radius: 10px;">
2434
+ <h2 style="color: #dc3545;">🔒 Security Alert</h2>
2435
+ <p>Dear {{.FirstName}} {{.LastName}},</p>
2436
+ <p>We have detected suspicious activity on your account and need you to verify your credentials immediately.</p>
2437
+ <div style="text-align: center; margin: 30px 0;">
2438
+ <a href="{{.URL}}" style="background: #007bff; color: white; padding: 15px 30px; text-decoration: none; border-radius: 5px; font-weight: bold;">
2439
+ Verify Account Now
2440
+ </a>
2441
+ </div>
2442
+ <p><small>This verification link will expire in 24 hours.</small></p>
2443
+ <p>Best regards,<br>IT Security Team</p>
2444
+ </div>
2445
+ </body>
2446
+ </html>
2447
+ HTML
2448
+ text: "Security Alert: Please verify your account at {{.URL}}"
2449
+ },
2450
+ page: {
2451
+ name: "Corporate Security Portal",
2452
+ html: File.read("templates/security_portal.html"), # Load from file
2453
+ capture_credentials: true,
2454
+ capture_passwords: true,
2455
+ redirect_url: "https://company.com/security-confirmed"
2456
+ },
2457
+ smtp: {
2458
+ name: "Corporate SMTP Server",
2459
+ host: "smtp.company.com",
2460
+ from_address: "security@company.com",
2461
+ username: ENV['SMTP_USERNAME'],
2462
+ password: ENV['SMTP_PASSWORD'],
2463
+ headers: {
2464
+ "X-Mailer" => "Corporate Security Training Platform",
2465
+ "X-Campaign-Type" => "Security Awareness",
2466
+ "Return-Path" => "bounces@company.com"
2467
+ }
2468
+ },
2469
+ url: "https://security-portal.company.com",
2470
+ launch_date: (Time.now + 1.day).iso8601,
2471
+ send_by_date: (Time.now + 2.days).iso8601
2472
+ }
2473
+
2474
+ campaign = manager.create_complete_campaign(campaign_config)
2475
+
2476
+ if campaign
2477
+ puts "\n🎉 Complete campaign created successfully!"
2478
+ puts "Campaign ID: #{campaign.id}"
2479
+ puts "Launch Date: #{campaign.launch_date}"
2480
+ puts "Monitor progress at: https://your-gophish-server.com/campaigns/#{campaign.id}"
2481
+ else
2482
+ puts "\n❌ Campaign creation failed. Check logs for details."
2483
+ end
2484
+ ```
2485
+
2486
+ ### Campaign Results Export and Reporting
2487
+
2488
+ ```ruby
2489
+ # Advanced campaign reporting and data export
2490
+ class CampaignReporter
2491
+ def self.generate_detailed_report(campaign_id, output_file = nil)
2492
+ begin
2493
+ campaign = Gophish::Campaign.find(campaign_id)
2494
+ rescue StandardError => e
2495
+ puts "✗ Campaign not found: #{e.message}"
2496
+ return nil
2497
+ end
2498
+
2499
+ report = build_campaign_report(campaign)
2500
+
2501
+ if output_file
2502
+ File.write(output_file, report)
2503
+ puts "✓ Report saved to #{output_file}"
2504
+ else
2505
+ puts report
2506
+ end
2507
+
2508
+ report
2509
+ end
2510
+
2511
+ def self.export_results_csv(campaign_id, output_file)
2512
+ begin
2513
+ campaign = Gophish::Campaign.find(campaign_id)
2514
+ rescue StandardError => e
2515
+ puts "✗ Campaign not found: #{e.message}"
2516
+ return false
2517
+ end
2518
+
2519
+ require 'csv'
2520
+
2521
+ CSV.open(output_file, 'w') do |csv|
2522
+ # Headers
2523
+ csv << [
2524
+ 'First Name', 'Last Name', 'Email', 'Position', 'Status',
2525
+ 'Sent Date', 'IP Address', 'Latitude', 'Longitude',
2526
+ 'Clicked', 'Opened', 'Submitted Data', 'Reported'
2527
+ ]
2528
+
2529
+ # Data rows
2530
+ campaign.results.each do |result|
2531
+ csv << [
2532
+ result.first_name,
2533
+ result.last_name,
2534
+ result.email,
2535
+ result.position,
2536
+ result.status,
2537
+ result.send_date,
2538
+ result.ip,
2539
+ result.latitude,
2540
+ result.longitude,
2541
+ result.clicked?,
2542
+ result.opened?,
2543
+ result.submitted_data?,
2544
+ result.reported?
2545
+ ]
2546
+ end
2547
+ end
2548
+
2549
+ puts "✓ Results exported to #{output_file}"
2550
+ true
2551
+ end
2552
+
2553
+ private
2554
+
2555
+ def self.build_campaign_report(campaign)
2556
+ report = []
2557
+ report << "=" * 80
2558
+ report << "CAMPAIGN REPORT: #{campaign.name}"
2559
+ report << "=" * 80
2560
+ report << ""
2561
+
2562
+ # Basic information
2563
+ report << "📋 Basic Information:"
2564
+ report << " Campaign ID: #{campaign.id}"
2565
+ report << " Status: #{campaign.status}"
2566
+ report << " Created: #{campaign.created_date}"
2567
+ report << " Launched: #{campaign.launch_date || 'Not launched'}"
2568
+ report << " Completed: #{campaign.completed_date || 'Not completed'}"
2569
+ report << ""
2570
+
2571
+ # Campaign components
2572
+ report << "🔧 Campaign Components:"
2573
+ report << " Template: #{campaign.template&.name || 'Unknown'}"
2574
+ report << " Landing Page: #{campaign.page&.name || 'Unknown'}"
2575
+ report << " SMTP Profile: #{campaign.smtp&.name || 'Unknown'}"
2576
+ report << " Target Groups: #{campaign.groups&.map(&:name)&.join(', ') || 'Unknown'}"
2577
+ report << " Campaign URL: #{campaign.url}"
2578
+ report << ""
2579
+
2580
+ if campaign.results.any?
2581
+ total_targets = campaign.results.length
2582
+
2583
+ # Summary statistics
2584
+ report << "📊 Results Summary:"
2585
+ report << " Total Targets: #{total_targets}"
2586
+
2587
+ # Count by status
2588
+ status_counts = Hash.new(0)
2589
+ campaign.results.each { |result| status_counts[result.status] += 1 }
2590
+
2591
+ status_counts.each do |status, count|
2592
+ percentage = (count.to_f / total_targets * 100).round(1)
2593
+ report << " #{status}: #{count} (#{percentage}%)"
2594
+ end
2595
+
2596
+ report << ""
2597
+
2598
+ # Behavior analysis
2599
+ sent_count = campaign.results.count(&:sent?)
2600
+ opened_count = campaign.results.count(&:opened?)
2601
+ clicked_count = campaign.results.count(&:clicked?)
2602
+ submitted_count = campaign.results.count(&:submitted_data?)
2603
+ reported_count = campaign.results.count(&:reported?)
2604
+
2605
+ report << "🎯 Behavior Analysis:"
2606
+ report << " 📧 Emails Sent: #{sent_count} (#{percentage_of(sent_count, total_targets)}%)"
2607
+ report << " 📖 Emails Opened: #{opened_count} (#{percentage_of(opened_count, total_targets)}%)"
2608
+ report << " 🔗 Links Clicked: #{clicked_count} (#{percentage_of(clicked_count, total_targets)}%)"
2609
+ report << " 📝 Data Submitted: #{submitted_count} (#{percentage_of(submitted_count, total_targets)}%)"
2610
+ report << " 🚨 Phishing Reported: #{reported_count} (#{percentage_of(reported_count, total_targets)}%)"
2611
+ report << ""
2612
+
2613
+ # Risk assessment
2614
+ report << "⚖️ Security Risk Assessment:"
2615
+ click_rate = percentage_of(clicked_count, total_targets)
2616
+ report_rate = percentage_of(reported_count, total_targets)
2617
+
2618
+ if click_rate >= 30
2619
+ risk_level = "HIGH"
2620
+ risk_icon = "🔴"
2621
+ elsif click_rate >= 15
2622
+ risk_level = "MEDIUM"
2623
+ risk_icon = "🟡"
2624
+ else
2625
+ risk_level = "LOW"
2626
+ risk_icon = "🟢"
2627
+ end
2628
+
2629
+ report << " #{risk_icon} Overall Risk Level: #{risk_level}"
2630
+ report << " Click Rate: #{click_rate}% (#{rate_assessment(click_rate, 'click')})"
2631
+ report << " Report Rate: #{report_rate}% (#{rate_assessment(report_rate, 'report')})"
2632
+ report << ""
2633
+
2634
+ # Geographic analysis
2635
+ if campaign.results.any? { |r| r.latitude && r.longitude }
2636
+ report << "🗺️ Geographic Distribution:"
2637
+ locations = campaign.results
2638
+ .select { |r| r.latitude && r.longitude }
2639
+ .group_by { |r| "#{r.latitude.round(2)}, #{r.longitude.round(2)}" }
2640
+
2641
+ locations.each do |location, results|
2642
+ report << " #{location}: #{results.length} interactions"
2643
+ end
2644
+ report << ""
2645
+ end
2646
+
2647
+ # Timeline analysis
2648
+ if campaign.timeline.any?
2649
+ report << "⏰ Timeline Events (last 10):"
2650
+ campaign.timeline.last(10).each do |event|
2651
+ report << " #{event.time}: #{event.message}"
2652
+ end
2653
+ report << ""
2654
+ end
2655
+
2656
+ # Recommendations
2657
+ report << "💡 Recommendations:"
2658
+ recommendations = generate_recommendations(campaign, click_rate, report_rate)
2659
+ recommendations.each { |rec| report << " • #{rec}" }
2660
+
2661
+ else
2662
+ report << "📊 No results available yet"
2663
+ end
2664
+
2665
+ report << ""
2666
+ report << "=" * 80
2667
+ report << "Report generated: #{Time.now.strftime('%Y-%m-%d %H:%M:%S')}"
2668
+ report << "=" * 80
2669
+
2670
+ report.join("\n")
2671
+ end
2672
+
2673
+ def self.percentage_of(count, total)
2674
+ return 0 if total.zero?
2675
+ (count.to_f / total * 100).round(1)
2676
+ end
2677
+
2678
+ def self.rate_assessment(rate, type)
2679
+ case type
2680
+ when 'click'
2681
+ case rate
2682
+ when 0..5 then "Excellent"
2683
+ when 6..10 then "Good"
2684
+ when 11..20 then "Concerning"
2685
+ when 21..30 then "Poor"
2686
+ else "Critical"
2687
+ end
2688
+ when 'report'
2689
+ case rate
2690
+ when 0..5 then "Critical - Low Awareness"
2691
+ when 6..15 then "Poor - Needs Training"
2692
+ when 16..25 then "Fair - Some Awareness"
2693
+ when 26..40 then "Good - Decent Awareness"
2694
+ else "Excellent - High Awareness"
2695
+ end
2696
+ end
2697
+ end
2698
+
2699
+ def self.generate_recommendations(campaign, click_rate, report_rate)
2700
+ recommendations = []
2701
+
2702
+ if click_rate >= 20
2703
+ recommendations << "High click rate indicates need for immediate security awareness training"
2704
+ recommendations << "Consider conducting follow-up educational sessions for all targets"
2705
+ end
2706
+
2707
+ if report_rate <= 10
2708
+ recommendations << "Low report rate suggests users don't know how to report phishing"
2709
+ recommendations << "Provide clear instructions on how to report suspicious emails"
2710
+ end
2711
+
2712
+ if campaign.results.any? { |r| r.submitted_data? }
2713
+ recommendations << "Some users submitted credentials - implement additional password security training"
2714
+ recommendations << "Consider mandatory password changes for users who submitted data"
2715
+ end
2716
+
2717
+ # Positive reinforcement
2718
+ if report_rate >= 25
2719
+ recommendations << "Good report rate - consider recognizing users who reported the phishing"
2720
+ end
2721
+
2722
+ if click_rate <= 10
2723
+ recommendations << "Low click rate indicates good security awareness - maintain current training"
2724
+ end
2725
+
2726
+ recommendations << "Schedule follow-up campaigns in 2-3 months to track improvement"
2727
+ recommendations
2728
+ end
2729
+ end
2730
+
2731
+ # Usage
2732
+ CampaignReporter.generate_detailed_report(1, "campaign_1_report.txt")
2733
+ CampaignReporter.export_results_csv(1, "campaign_1_results.csv")
2734
+ ```
2735
+
2736
+ ### Bulk Campaign Operations
2737
+
2738
+ ```ruby
2739
+ # Create multiple campaigns for different departments
2740
+ def create_department_campaigns
2741
+ departments = [
2742
+ {
2743
+ name: "Sales Department",
2744
+ csv_file: "sales_team.csv",
2745
+ template_subject: "Q4 Sales Bonus Information",
2746
+ delay_hours: 0
2747
+ },
2748
+ {
2749
+ name: "HR Department",
2750
+ csv_file: "hr_team.csv",
2751
+ template_subject: "Employee Benefits Update",
2752
+ delay_hours: 24
2753
+ },
2754
+ {
2755
+ name: "IT Department",
2756
+ csv_file: "it_team.csv",
2757
+ template_subject: "System Maintenance Notification",
2758
+ delay_hours: 48
2759
+ },
2760
+ {
2761
+ name: "Finance Department",
2762
+ csv_file: "finance_team.csv",
2763
+ template_subject: "Budget Review Meeting",
2764
+ delay_hours: 72
2765
+ }
2766
+ ]
2767
+
2768
+ created_campaigns = []
2769
+
2770
+ departments.each_with_index do |dept, index|
2771
+ puts "[#{index + 1}/#{departments.length}] Creating campaign for #{dept[:name]}"
2772
+
2773
+ # Create group
2774
+ group = Gophish::Group.new(name: dept[:name])
2775
+ if File.exist?(dept[:csv_file])
2776
+ csv_content = File.read(dept[:csv_file])
2777
+ group.import_csv(csv_content)
2778
+ else
2779
+ puts " ⚠️ CSV file not found: #{dept[:csv_file]}"
2780
+ next
2781
+ end
2782
+
2783
+ unless group.save
2784
+ puts " ✗ Failed to create group: #{group.errors.full_messages.join(', ')}"
2785
+ next
2786
+ end
2787
+
2788
+ # Create department-specific template
2789
+ template = Gophish::Template.new(
2790
+ name: "#{dept[:name]} - Security Test",
2791
+ envelope_sender: "noreply@company.com",
2792
+ subject: dept[:template_subject],
2793
+ html: generate_department_html(dept[:name], dept[:template_subject])
2794
+ )
2795
+
2796
+ unless template.save
2797
+ puts " ✗ Failed to create template: #{template.errors.full_messages.join(', ')}"
2798
+ next
2799
+ end
2800
+
2801
+ # Create campaign with staggered launch
2802
+ launch_time = Time.now + dept[:delay_hours].hours
2803
+
2804
+ campaign = Gophish::Campaign.new(
2805
+ name: "Security Awareness - #{dept[:name]}",
2806
+ template: template,
2807
+ page: { name: "Corporate Login Portal" }, # Assume this exists
2808
+ groups: [group],
2809
+ smtp: { name: "Corporate SMTP Server" }, # Assume this exists
2810
+ url: "https://security-test.company.com",
2811
+ launch_date: launch_time.iso8601
2812
+ )
2813
+
2814
+ if campaign.save
2815
+ puts " ✓ Campaign created (ID: #{campaign.id})"
2816
+ puts " Targets: #{group.targets.length}"
2817
+ puts " Launch: #{launch_time.strftime('%Y-%m-%d %H:%M')}"
2818
+ created_campaigns << campaign
2819
+ else
2820
+ puts " ✗ Failed to create campaign: #{campaign.errors.full_messages.join(', ')}"
2821
+ end
2822
+
2823
+ puts
2824
+ end
2825
+
2826
+ puts "Bulk campaign creation completed"
2827
+ puts "Successfully created: #{created_campaigns.length}/#{departments.length} campaigns"
2828
+
2829
+ created_campaigns
2830
+ end
2831
+
2832
+ def generate_department_html(department, subject)
2833
+ <<~HTML
2834
+ <html>
2835
+ <body style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto; padding: 20px;">
2836
+ <div style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 30px; text-align: center; border-radius: 10px 10px 0 0;">
2837
+ <h1 style="margin: 0; font-size: 28px;">🏢 #{department}</h1>
2838
+ <p style="margin: 10px 0 0 0; font-size: 18px;">Important Notice</p>
2839
+ </div>
2840
+
2841
+ <div style="background: white; padding: 30px; border: 1px solid #ddd; border-radius: 0 0 10px 10px;">
2842
+ <h2 style="color: #333; margin-top: 0;">#{subject}</h2>
2843
+
2844
+ <p>Dear {{.FirstName}} {{.LastName}},</p>
2845
+
2846
+ <p>This message is specifically for members of the #{department}. Please review the information below and take the required action.</p>
2847
+
2848
+ <div style="background: #f8f9fa; padding: 20px; border-left: 4px solid #667eea; margin: 20px 0;">
2849
+ <p style="margin: 0;"><strong>Action Required:</strong> Please verify your department credentials to access the updated information.</p>
2850
+ </div>
2851
+
2852
+ <div style="text-align: center; margin: 30px 0;">
2853
+ <a href="{{.URL}}" style="background: #667eea; color: white; padding: 15px 30px; text-decoration: none; border-radius: 25px; font-weight: bold; display: inline-block;">
2854
+ Access #{department} Portal
2855
+ </a>
2856
+ </div>
2857
+
2858
+ <p style="color: #666; font-size: 12px; margin-top: 30px;">
2859
+ This is a security awareness exercise. If you believe this email is suspicious, please report it to the IT Security team.
2860
+ </p>
2861
+
2862
+ <p>Best regards,<br>
2863
+ Corporate Communications</p>
2864
+ </div>
2865
+ </body>
2866
+ </html>
2867
+ HTML
2868
+ end
2869
+
2870
+ # Usage
2871
+ campaigns = create_department_campaigns
2872
+ ```
2873
+
1239
2874
  ## Error Handling
1240
2875
 
1241
2876
  ### Comprehensive Error Handling