gophish-ruby 0.4.0 → 1.0.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +99 -2
- data/README.md +491 -1
- data/docs/API_REFERENCE.md +925 -0
- data/docs/EXAMPLES.md +1635 -0
- data/docs/GETTING_STARTED.md +364 -1
- data/lib/gophish/campaign.rb +330 -0
- data/lib/gophish/template.rb +7 -2
- data/lib/gophish/version.rb +1 -1
- data/lib/gophish-ruby.rb +1 -0
- metadata +2 -1
data/docs/API_REFERENCE.md
CHANGED
@@ -9,6 +9,8 @@ This document provides detailed API reference for the Gophish Ruby SDK.
|
|
9
9
|
- [Group Class](#group-class)
|
10
10
|
- [Template Class](#template-class)
|
11
11
|
- [Page Class](#page-class)
|
12
|
+
- [SMTP Class](#smtp-class)
|
13
|
+
- [Campaign Class](#campaign-class)
|
12
14
|
- [Error Handling](#error-handling)
|
13
15
|
- [Examples](#examples)
|
14
16
|
|
@@ -407,6 +409,7 @@ The `Gophish::Template` class represents an email template in Gophish.
|
|
407
409
|
|-----------|------|----------|-------------|
|
408
410
|
| `id` | Integer | No | Unique template identifier (set by server) |
|
409
411
|
| `name` | String | Yes | Template name |
|
412
|
+
| `envelope_sender` | String | No | Envelope sender email address for advanced delivery control |
|
410
413
|
| `subject` | String | No | Email subject line |
|
411
414
|
| `text` | String | No | Plain text email content |
|
412
415
|
| `html` | String | No | HTML email content |
|
@@ -531,6 +534,22 @@ Get the number of attachments.
|
|
531
534
|
puts "Attachments: #{template.attachment_count}"
|
532
535
|
```
|
533
536
|
|
537
|
+
##### `#has_envelope_sender?`
|
538
|
+
|
539
|
+
Check if template has an envelope sender configured.
|
540
|
+
|
541
|
+
**Returns:** Boolean
|
542
|
+
|
543
|
+
**Example:**
|
544
|
+
|
545
|
+
```ruby
|
546
|
+
template = Gophish::Template.new(envelope_sender: "noreply@company.com")
|
547
|
+
puts template.has_envelope_sender? # => true
|
548
|
+
|
549
|
+
template = Gophish::Template.new
|
550
|
+
puts template.has_envelope_sender? # => false
|
551
|
+
```
|
552
|
+
|
534
553
|
#### Usage Examples
|
535
554
|
|
536
555
|
##### Create a Template
|
@@ -568,6 +587,33 @@ template.add_attachment(image_content, "image/png", "logo.png")
|
|
568
587
|
template.save
|
569
588
|
```
|
570
589
|
|
590
|
+
##### Template with Envelope Sender
|
591
|
+
|
592
|
+
```ruby
|
593
|
+
# Create template with envelope sender for better deliverability
|
594
|
+
template = Gophish::Template.new(
|
595
|
+
name: "Corporate IT Update",
|
596
|
+
envelope_sender: "noreply@company.com", # Separate envelope sender
|
597
|
+
subject: "Important IT Security Update",
|
598
|
+
html: <<~HTML
|
599
|
+
<html>
|
600
|
+
<body>
|
601
|
+
<h1>IT Security Department</h1>
|
602
|
+
<p>We've detected suspicious activity on your account.</p>
|
603
|
+
<p><a href="{{.URL}}">Click here to verify your account</a></p>
|
604
|
+
<p>Thank you,<br>IT Security Team</p>
|
605
|
+
</body>
|
606
|
+
</html>
|
607
|
+
HTML
|
608
|
+
)
|
609
|
+
|
610
|
+
if template.save
|
611
|
+
puts "Template created with envelope sender"
|
612
|
+
puts "Envelope sender: #{template.envelope_sender}"
|
613
|
+
puts "Has envelope sender: #{template.has_envelope_sender?}"
|
614
|
+
end
|
615
|
+
```
|
616
|
+
|
571
617
|
##### Import from Email
|
572
618
|
|
573
619
|
```ruby
|
@@ -944,6 +990,885 @@ if page.destroy
|
|
944
990
|
end
|
945
991
|
```
|
946
992
|
|
993
|
+
## SMTP Class
|
994
|
+
|
995
|
+
The `Gophish::Smtp` class represents an SMTP sending profile in Gophish campaigns.
|
996
|
+
|
997
|
+
### Class: `Gophish::Smtp < Gophish::Base`
|
998
|
+
|
999
|
+
#### Attributes
|
1000
|
+
|
1001
|
+
| Attribute | Type | Required | Description |
|
1002
|
+
|-----------|------|----------|-------------|
|
1003
|
+
| `id` | Integer | No | Unique SMTP profile identifier (set by server) |
|
1004
|
+
| `name` | String | Yes | SMTP profile name |
|
1005
|
+
| `username` | String | No | SMTP authentication username |
|
1006
|
+
| `password` | String | No | SMTP authentication password |
|
1007
|
+
| `host` | String | Yes | SMTP server hostname |
|
1008
|
+
| `interface_type` | String | No | Interface type (default: "SMTP") |
|
1009
|
+
| `from_address` | String | Yes | From email address (must be valid email format) |
|
1010
|
+
| `ignore_cert_errors` | Boolean | No | Whether to ignore SSL certificate errors (default: false) |
|
1011
|
+
| `modified_date` | String | No | Last modification timestamp (set by server) |
|
1012
|
+
| `headers` | Array | No | Array of custom header hashes |
|
1013
|
+
|
1014
|
+
#### Header Structure
|
1015
|
+
|
1016
|
+
Each header in the `headers` array must have:
|
1017
|
+
|
1018
|
+
| Field | Type | Required | Description |
|
1019
|
+
|-------|------|----------|-------------|
|
1020
|
+
| `key` | String | Yes | Header name |
|
1021
|
+
| `value` | String | Yes | Header value |
|
1022
|
+
|
1023
|
+
#### Validations
|
1024
|
+
|
1025
|
+
- `name` must be present
|
1026
|
+
- `host` must be present
|
1027
|
+
- `from_address` must be present and have valid email format
|
1028
|
+
- Each header must be a Hash with both `key` and `value`
|
1029
|
+
|
1030
|
+
#### Instance Methods
|
1031
|
+
|
1032
|
+
##### `#add_header(key, value)`
|
1033
|
+
|
1034
|
+
Add a custom header to the SMTP profile.
|
1035
|
+
|
1036
|
+
**Parameters:**
|
1037
|
+
|
1038
|
+
- `key` (String) - Header name
|
1039
|
+
- `value` (String) - Header value
|
1040
|
+
|
1041
|
+
**Returns:** Void
|
1042
|
+
|
1043
|
+
**Side Effects:**
|
1044
|
+
|
1045
|
+
- Adds header to `headers` array
|
1046
|
+
- Marks `headers` attribute as changed
|
1047
|
+
|
1048
|
+
**Example:**
|
1049
|
+
|
1050
|
+
```ruby
|
1051
|
+
smtp = Gophish::Smtp.new(name: "Test", host: "smtp.test.com", from_address: "test@example.com")
|
1052
|
+
smtp.add_header("X-Mailer", "Company Security Tool")
|
1053
|
+
smtp.add_header("Return-Path", "bounces@company.com")
|
1054
|
+
```
|
1055
|
+
|
1056
|
+
##### `#remove_header(key)`
|
1057
|
+
|
1058
|
+
Remove a header by key name.
|
1059
|
+
|
1060
|
+
**Parameters:**
|
1061
|
+
|
1062
|
+
- `key` (String) - Header name to remove
|
1063
|
+
|
1064
|
+
**Returns:** Void
|
1065
|
+
|
1066
|
+
**Side Effects:**
|
1067
|
+
|
1068
|
+
- Removes matching header(s) from `headers` array
|
1069
|
+
- Marks `headers` attribute as changed if any were removed
|
1070
|
+
|
1071
|
+
**Example:**
|
1072
|
+
|
1073
|
+
```ruby
|
1074
|
+
smtp.remove_header("X-Mailer")
|
1075
|
+
```
|
1076
|
+
|
1077
|
+
##### `#has_headers?`
|
1078
|
+
|
1079
|
+
Check if SMTP profile has any custom headers.
|
1080
|
+
|
1081
|
+
**Returns:** Boolean
|
1082
|
+
|
1083
|
+
**Example:**
|
1084
|
+
|
1085
|
+
```ruby
|
1086
|
+
if smtp.has_headers?
|
1087
|
+
puts "SMTP profile has #{smtp.header_count} custom headers"
|
1088
|
+
end
|
1089
|
+
```
|
1090
|
+
|
1091
|
+
##### `#header_count`
|
1092
|
+
|
1093
|
+
Get the number of custom headers.
|
1094
|
+
|
1095
|
+
**Returns:** Integer
|
1096
|
+
|
1097
|
+
**Example:**
|
1098
|
+
|
1099
|
+
```ruby
|
1100
|
+
puts "Custom headers: #{smtp.header_count}"
|
1101
|
+
```
|
1102
|
+
|
1103
|
+
##### `#has_authentication?`
|
1104
|
+
|
1105
|
+
Check if SMTP profile uses authentication (both username and password are present).
|
1106
|
+
|
1107
|
+
**Returns:** Boolean
|
1108
|
+
|
1109
|
+
**Example:**
|
1110
|
+
|
1111
|
+
```ruby
|
1112
|
+
if smtp.has_authentication?
|
1113
|
+
puts "SMTP profile uses authentication"
|
1114
|
+
end
|
1115
|
+
```
|
1116
|
+
|
1117
|
+
##### `#ignores_cert_errors?`
|
1118
|
+
|
1119
|
+
Check if SMTP profile ignores SSL certificate errors.
|
1120
|
+
|
1121
|
+
**Returns:** Boolean
|
1122
|
+
|
1123
|
+
**Example:**
|
1124
|
+
|
1125
|
+
```ruby
|
1126
|
+
if smtp.ignores_cert_errors?
|
1127
|
+
puts "⚠️ SMTP profile ignores SSL certificate errors"
|
1128
|
+
end
|
1129
|
+
```
|
1130
|
+
|
1131
|
+
#### Usage Examples
|
1132
|
+
|
1133
|
+
##### Create a Basic SMTP Profile
|
1134
|
+
|
1135
|
+
```ruby
|
1136
|
+
smtp = Gophish::Smtp.new(
|
1137
|
+
name: "Company Mail Server",
|
1138
|
+
host: "smtp.company.com",
|
1139
|
+
from_address: "security@company.com"
|
1140
|
+
)
|
1141
|
+
|
1142
|
+
if smtp.save
|
1143
|
+
puts "SMTP profile created with ID: #{smtp.id}"
|
1144
|
+
else
|
1145
|
+
puts "Failed to create SMTP profile: #{smtp.errors.full_messages}"
|
1146
|
+
end
|
1147
|
+
```
|
1148
|
+
|
1149
|
+
##### Create SMTP Profile with Authentication
|
1150
|
+
|
1151
|
+
```ruby
|
1152
|
+
smtp = Gophish::Smtp.new(
|
1153
|
+
name: "Gmail SMTP",
|
1154
|
+
host: "smtp.gmail.com",
|
1155
|
+
from_address: "phishing.test@company.com",
|
1156
|
+
username: "smtp_user@company.com",
|
1157
|
+
password: "app_specific_password",
|
1158
|
+
ignore_cert_errors: false
|
1159
|
+
)
|
1160
|
+
|
1161
|
+
puts "Uses authentication: #{smtp.has_authentication?}"
|
1162
|
+
puts "Ignores cert errors: #{smtp.ignores_cert_errors?}"
|
1163
|
+
|
1164
|
+
smtp.save
|
1165
|
+
```
|
1166
|
+
|
1167
|
+
##### SMTP Profile with Custom Headers
|
1168
|
+
|
1169
|
+
```ruby
|
1170
|
+
smtp = Gophish::Smtp.new(
|
1171
|
+
name: "Custom Headers SMTP",
|
1172
|
+
host: "mail.company.com",
|
1173
|
+
from_address: "security@company.com"
|
1174
|
+
)
|
1175
|
+
|
1176
|
+
# Add custom headers for routing and identification
|
1177
|
+
smtp.add_header("X-Mailer", "Gophish Security Training")
|
1178
|
+
smtp.add_header("X-Campaign-Type", "Phishing Simulation")
|
1179
|
+
smtp.add_header("X-Company", "ACME Corp")
|
1180
|
+
smtp.add_header("Return-Path", "bounces@company.com")
|
1181
|
+
|
1182
|
+
puts "Header count: #{smtp.header_count}"
|
1183
|
+
puts "Headers: #{smtp.headers.inspect}"
|
1184
|
+
|
1185
|
+
smtp.save
|
1186
|
+
```
|
1187
|
+
|
1188
|
+
##### Update SMTP Profile
|
1189
|
+
|
1190
|
+
```ruby
|
1191
|
+
smtp = Gophish::Smtp.find(1)
|
1192
|
+
|
1193
|
+
# Update basic settings
|
1194
|
+
smtp.name = "Updated Company SMTP"
|
1195
|
+
smtp.ignore_cert_errors = true
|
1196
|
+
|
1197
|
+
# Add new header
|
1198
|
+
smtp.add_header("X-Priority", "High")
|
1199
|
+
|
1200
|
+
# Remove old header
|
1201
|
+
smtp.remove_header("X-Campaign-Type")
|
1202
|
+
|
1203
|
+
if smtp.save
|
1204
|
+
puts "SMTP profile updated successfully"
|
1205
|
+
puts "Header count: #{smtp.header_count}"
|
1206
|
+
end
|
1207
|
+
```
|
1208
|
+
|
1209
|
+
##### SMTP Profile Validation
|
1210
|
+
|
1211
|
+
```ruby
|
1212
|
+
# Invalid SMTP profile (missing required fields)
|
1213
|
+
smtp = Gophish::Smtp.new
|
1214
|
+
|
1215
|
+
unless smtp.valid?
|
1216
|
+
puts "Validation errors:"
|
1217
|
+
smtp.errors.full_messages.each { |msg| puts " - #{msg}" }
|
1218
|
+
# => ["Name can't be blank", "Host can't be blank", "From address can't be blank"]
|
1219
|
+
end
|
1220
|
+
|
1221
|
+
# Invalid email format
|
1222
|
+
smtp = Gophish::Smtp.new(
|
1223
|
+
name: "Test SMTP",
|
1224
|
+
host: "smtp.test.com",
|
1225
|
+
from_address: "invalid-email"
|
1226
|
+
)
|
1227
|
+
|
1228
|
+
unless smtp.valid?
|
1229
|
+
puts "Email validation error:"
|
1230
|
+
puts smtp.errors[:from_address]
|
1231
|
+
# => ["must be a valid email format (email@domain.com)"]
|
1232
|
+
end
|
1233
|
+
|
1234
|
+
# Valid SMTP profile
|
1235
|
+
smtp = Gophish::Smtp.new(
|
1236
|
+
name: "Valid SMTP",
|
1237
|
+
host: "smtp.example.com",
|
1238
|
+
from_address: "valid@example.com"
|
1239
|
+
)
|
1240
|
+
|
1241
|
+
puts smtp.valid? # => true
|
1242
|
+
```
|
1243
|
+
|
1244
|
+
##### Comprehensive SMTP Configuration
|
1245
|
+
|
1246
|
+
```ruby
|
1247
|
+
# Production-ready SMTP configuration
|
1248
|
+
smtp = Gophish::Smtp.new(
|
1249
|
+
name: "Production Mail Server",
|
1250
|
+
host: "smtp.company.com",
|
1251
|
+
from_address: "security-training@company.com",
|
1252
|
+
username: "smtp_service_account",
|
1253
|
+
password: ENV['SMTP_PASSWORD'], # Use environment variables for secrets
|
1254
|
+
ignore_cert_errors: false, # Always verify certificates in production
|
1255
|
+
interface_type: "SMTP"
|
1256
|
+
)
|
1257
|
+
|
1258
|
+
# Add headers for better deliverability and tracking
|
1259
|
+
smtp.add_header("X-Mailer", "Gophish Security Training Platform")
|
1260
|
+
smtp.add_header("X-Department", "Information Security")
|
1261
|
+
smtp.add_header("Return-Path", "bounces+security@company.com")
|
1262
|
+
smtp.add_header("Reply-To", "security-team@company.com")
|
1263
|
+
|
1264
|
+
# Validate before saving
|
1265
|
+
if smtp.valid?
|
1266
|
+
if smtp.save
|
1267
|
+
puts "✓ SMTP profile created successfully!"
|
1268
|
+
puts " ID: #{smtp.id}"
|
1269
|
+
puts " Name: #{smtp.name}"
|
1270
|
+
puts " Host: #{smtp.host}"
|
1271
|
+
puts " From: #{smtp.from_address}"
|
1272
|
+
puts " Authentication: #{smtp.has_authentication? ? 'Yes' : 'No'}"
|
1273
|
+
puts " Custom Headers: #{smtp.header_count}"
|
1274
|
+
puts " SSL Verification: #{smtp.ignore_cert_errors? ? 'Disabled' : 'Enabled'}"
|
1275
|
+
else
|
1276
|
+
puts "✗ Failed to save SMTP profile:"
|
1277
|
+
smtp.errors.full_messages.each { |msg| puts " - #{msg}" }
|
1278
|
+
end
|
1279
|
+
else
|
1280
|
+
puts "✗ SMTP validation failed:"
|
1281
|
+
smtp.errors.full_messages.each { |msg| puts " - #{msg}" }
|
1282
|
+
end
|
1283
|
+
```
|
1284
|
+
|
1285
|
+
##### Delete SMTP Profile
|
1286
|
+
|
1287
|
+
```ruby
|
1288
|
+
smtp = Gophish::Smtp.find(1)
|
1289
|
+
|
1290
|
+
if smtp.destroy
|
1291
|
+
puts "SMTP profile deleted successfully"
|
1292
|
+
puts "Profile frozen: #{smtp.frozen?}" # => true
|
1293
|
+
end
|
1294
|
+
```
|
1295
|
+
|
1296
|
+
##### Security Considerations for SMTP
|
1297
|
+
|
1298
|
+
```ruby
|
1299
|
+
smtp = Gophish::Smtp.find(1)
|
1300
|
+
|
1301
|
+
# Security checks
|
1302
|
+
puts "🔍 Security Assessment:"
|
1303
|
+
puts " Authentication required: #{smtp.has_authentication? ? '✓' : '✗'}"
|
1304
|
+
puts " SSL verification: #{smtp.ignore_cert_errors? ? '✗ DISABLED' : '✓ ENABLED'}"
|
1305
|
+
puts " From address domain: #{smtp.from_address.split('@').last}"
|
1306
|
+
|
1307
|
+
if smtp.has_headers?
|
1308
|
+
puts " Custom headers (#{smtp.header_count}):"
|
1309
|
+
smtp.headers.each do |header|
|
1310
|
+
puts " #{header[:key] || header['key']}: #{header[:value] || header['value']}"
|
1311
|
+
end
|
1312
|
+
end
|
1313
|
+
|
1314
|
+
# Warn about potential security issues
|
1315
|
+
if smtp.ignore_cert_errors?
|
1316
|
+
puts "⚠️ WARNING: SSL certificate verification is disabled!"
|
1317
|
+
puts " This may allow man-in-the-middle attacks in production."
|
1318
|
+
end
|
1319
|
+
|
1320
|
+
unless smtp.has_authentication?
|
1321
|
+
puts "ℹ️ INFO: No authentication configured."
|
1322
|
+
puts " Ensure your SMTP server allows unauthenticated sending."
|
1323
|
+
end
|
1324
|
+
```
|
1325
|
+
|
1326
|
+
## Campaign Class
|
1327
|
+
|
1328
|
+
The `Gophish::Campaign` class represents a phishing campaign that orchestrates the sending of phishing emails to target groups using templates, landing pages, and SMTP profiles.
|
1329
|
+
|
1330
|
+
### Class: `Gophish::Campaign < Gophish::Base`
|
1331
|
+
|
1332
|
+
#### Attributes
|
1333
|
+
|
1334
|
+
| Attribute | Type | Required | Description |
|
1335
|
+
|-----------|------|----------|-------------|
|
1336
|
+
| `id` | Integer | No | Unique campaign identifier (set by server) |
|
1337
|
+
| `name` | String | Yes | Campaign name |
|
1338
|
+
| `created_date` | String | No | Campaign creation timestamp (set by server) |
|
1339
|
+
| `launch_date` | String | No | When the campaign should start sending emails |
|
1340
|
+
| `send_by_date` | String | No | Deadline for sending all campaign emails |
|
1341
|
+
| `completed_date` | String | No | When the campaign was completed (set by server) |
|
1342
|
+
| `template` | Hash/Object | Yes | Reference to email template (can be hash with name or Template instance) |
|
1343
|
+
| `page` | Hash/Object | Yes | Reference to landing page (can be hash with name or Page instance) |
|
1344
|
+
| `status` | String | No | Current campaign status (e.g., "In progress", "Completed") |
|
1345
|
+
| `results` | Array | No | Array of Result instances showing target interactions |
|
1346
|
+
| `groups` | Array | Yes | Array of target groups (can be hashes with names or Group instances) |
|
1347
|
+
| `timeline` | Array | No | Array of Event instances showing campaign timeline |
|
1348
|
+
| `smtp` | Hash/Object | Yes | Reference to SMTP profile (can be hash with name or Smtp instance) |
|
1349
|
+
| `url` | String | Yes | Base URL for the campaign |
|
1350
|
+
|
1351
|
+
#### Validations
|
1352
|
+
|
1353
|
+
- `name` must be present
|
1354
|
+
- `template` must be present (reference to existing template)
|
1355
|
+
- `page` must be present (reference to existing landing page)
|
1356
|
+
- `groups` must be present and non-empty (references to existing groups)
|
1357
|
+
- `smtp` must be present (reference to existing SMTP profile)
|
1358
|
+
- `url` must be present
|
1359
|
+
- All groups must have names if provided as hashes
|
1360
|
+
- All results must have email addresses
|
1361
|
+
- All timeline events must have time and message
|
1362
|
+
|
1363
|
+
#### Class Methods
|
1364
|
+
|
1365
|
+
##### `.get_results(id)`
|
1366
|
+
|
1367
|
+
Get campaign results by campaign ID without loading the full campaign object.
|
1368
|
+
|
1369
|
+
**Parameters:**
|
1370
|
+
|
1371
|
+
- `id` (Integer) - Campaign ID
|
1372
|
+
|
1373
|
+
**Returns:** Array of result hashes
|
1374
|
+
|
1375
|
+
**Raises:**
|
1376
|
+
|
1377
|
+
- `StandardError` if campaign not found or API request fails
|
1378
|
+
|
1379
|
+
**Example:**
|
1380
|
+
|
1381
|
+
```ruby
|
1382
|
+
results = Gophish::Campaign.get_results(1)
|
1383
|
+
puts "Campaign has #{results.length} results"
|
1384
|
+
```
|
1385
|
+
|
1386
|
+
##### `.get_summary(id)`
|
1387
|
+
|
1388
|
+
Get campaign summary statistics by ID without loading the full campaign object.
|
1389
|
+
|
1390
|
+
**Parameters:**
|
1391
|
+
|
1392
|
+
- `id` (Integer) - Campaign ID
|
1393
|
+
|
1394
|
+
**Returns:** Hash of summary statistics
|
1395
|
+
|
1396
|
+
**Raises:**
|
1397
|
+
|
1398
|
+
- `StandardError` if campaign not found or API request fails
|
1399
|
+
|
1400
|
+
**Example:**
|
1401
|
+
|
1402
|
+
```ruby
|
1403
|
+
summary = Gophish::Campaign.get_summary(1)
|
1404
|
+
puts "Campaign stats: #{summary['stats']}"
|
1405
|
+
```
|
1406
|
+
|
1407
|
+
##### `.complete(id)`
|
1408
|
+
|
1409
|
+
Complete a campaign by ID without loading the full campaign object.
|
1410
|
+
|
1411
|
+
**Parameters:**
|
1412
|
+
|
1413
|
+
- `id` (Integer) - Campaign ID
|
1414
|
+
|
1415
|
+
**Returns:** Hash with success status
|
1416
|
+
|
1417
|
+
**Raises:**
|
1418
|
+
|
1419
|
+
- `StandardError` if campaign not found or API request fails
|
1420
|
+
|
1421
|
+
**Example:**
|
1422
|
+
|
1423
|
+
```ruby
|
1424
|
+
result = Gophish::Campaign.complete(1)
|
1425
|
+
puts "Campaign completed: #{result['success']}"
|
1426
|
+
```
|
1427
|
+
|
1428
|
+
#### Instance Methods
|
1429
|
+
|
1430
|
+
##### `#get_results`
|
1431
|
+
|
1432
|
+
Get detailed campaign results for this campaign instance.
|
1433
|
+
|
1434
|
+
**Returns:** Array of parsed response data
|
1435
|
+
|
1436
|
+
**Example:**
|
1437
|
+
|
1438
|
+
```ruby
|
1439
|
+
campaign = Gophish::Campaign.find(1)
|
1440
|
+
results = campaign.get_results
|
1441
|
+
puts "Total targets: #{results.length}"
|
1442
|
+
```
|
1443
|
+
|
1444
|
+
##### `#get_summary`
|
1445
|
+
|
1446
|
+
Get campaign summary statistics for this campaign instance.
|
1447
|
+
|
1448
|
+
**Returns:** Hash of summary data
|
1449
|
+
|
1450
|
+
**Example:**
|
1451
|
+
|
1452
|
+
```ruby
|
1453
|
+
campaign = Gophish::Campaign.find(1)
|
1454
|
+
summary = campaign.get_summary
|
1455
|
+
puts "Campaign summary: #{summary}"
|
1456
|
+
```
|
1457
|
+
|
1458
|
+
##### `#complete!`
|
1459
|
+
|
1460
|
+
Complete the campaign and update the status attribute.
|
1461
|
+
|
1462
|
+
**Returns:** Hash with success status
|
1463
|
+
|
1464
|
+
**Side Effects:**
|
1465
|
+
|
1466
|
+
- Updates `status` attribute to 'Completed' on success
|
1467
|
+
|
1468
|
+
**Example:**
|
1469
|
+
|
1470
|
+
```ruby
|
1471
|
+
campaign = Gophish::Campaign.find(1)
|
1472
|
+
if campaign.in_progress?
|
1473
|
+
result = campaign.complete!
|
1474
|
+
puts "Campaign completed: #{result['success']}"
|
1475
|
+
puts "New status: #{campaign.status}"
|
1476
|
+
end
|
1477
|
+
```
|
1478
|
+
|
1479
|
+
##### `#in_progress?`
|
1480
|
+
|
1481
|
+
Check if campaign is currently running.
|
1482
|
+
|
1483
|
+
**Returns:** Boolean (true if status is "In progress")
|
1484
|
+
|
1485
|
+
**Example:**
|
1486
|
+
|
1487
|
+
```ruby
|
1488
|
+
if campaign.in_progress?
|
1489
|
+
puts "Campaign is actively running"
|
1490
|
+
end
|
1491
|
+
```
|
1492
|
+
|
1493
|
+
##### `#completed?`
|
1494
|
+
|
1495
|
+
Check if campaign has been completed.
|
1496
|
+
|
1497
|
+
**Returns:** Boolean (true if status is "Completed")
|
1498
|
+
|
1499
|
+
**Example:**
|
1500
|
+
|
1501
|
+
```ruby
|
1502
|
+
if campaign.completed?
|
1503
|
+
puts "Campaign has finished"
|
1504
|
+
end
|
1505
|
+
```
|
1506
|
+
|
1507
|
+
##### `#launched?`
|
1508
|
+
|
1509
|
+
Check if campaign has a launch date set.
|
1510
|
+
|
1511
|
+
**Returns:** Boolean (true if launch_date is not blank)
|
1512
|
+
|
1513
|
+
**Example:**
|
1514
|
+
|
1515
|
+
```ruby
|
1516
|
+
if campaign.launched?
|
1517
|
+
puts "Campaign launched at: #{campaign.launch_date}"
|
1518
|
+
end
|
1519
|
+
```
|
1520
|
+
|
1521
|
+
##### `#has_send_by_date?`
|
1522
|
+
|
1523
|
+
Check if campaign has a send-by deadline configured.
|
1524
|
+
|
1525
|
+
**Returns:** Boolean (true if send_by_date is not blank)
|
1526
|
+
|
1527
|
+
**Example:**
|
1528
|
+
|
1529
|
+
```ruby
|
1530
|
+
if campaign.has_send_by_date?
|
1531
|
+
puts "Must complete sending by: #{campaign.send_by_date}"
|
1532
|
+
end
|
1533
|
+
```
|
1534
|
+
|
1535
|
+
#### Nested Classes
|
1536
|
+
|
1537
|
+
##### `Gophish::Campaign::Result`
|
1538
|
+
|
1539
|
+
Represents individual target results within a campaign.
|
1540
|
+
|
1541
|
+
**Attributes:**
|
1542
|
+
|
1543
|
+
| Attribute | Type | Description |
|
1544
|
+
|-----------|------|-------------|
|
1545
|
+
| `id` | String | Result identifier |
|
1546
|
+
| `first_name` | String | Target's first name |
|
1547
|
+
| `last_name` | String | Target's last name |
|
1548
|
+
| `position` | String | Target's position |
|
1549
|
+
| `email` | String | Target's email address |
|
1550
|
+
| `status` | String | Current result status |
|
1551
|
+
| `ip` | String | IP address of target interactions |
|
1552
|
+
| `latitude` | Float | Geographic latitude |
|
1553
|
+
| `longitude` | Float | Geographic longitude |
|
1554
|
+
| `send_date` | String | When email was sent |
|
1555
|
+
| `reported` | Boolean | Whether target reported the email |
|
1556
|
+
| `modified_date` | String | Last modification timestamp |
|
1557
|
+
|
1558
|
+
**Instance Methods:**
|
1559
|
+
|
1560
|
+
- `#reported?` - Check if target reported the phishing email
|
1561
|
+
- `#clicked?` - Check if target clicked the phishing link (status == "Clicked Link")
|
1562
|
+
- `#opened?` - Check if target opened the email (status == "Email Opened")
|
1563
|
+
- `#sent?` - Check if email was sent to target (status == "Email Sent")
|
1564
|
+
- `#submitted_data?` - Check if target submitted data on landing page (status == "Submitted Data")
|
1565
|
+
|
1566
|
+
##### `Gophish::Campaign::Event`
|
1567
|
+
|
1568
|
+
Represents timeline events within a campaign.
|
1569
|
+
|
1570
|
+
**Attributes:**
|
1571
|
+
|
1572
|
+
| Attribute | Type | Description |
|
1573
|
+
|-----------|------|-------------|
|
1574
|
+
| `email` | String | Target email associated with event |
|
1575
|
+
| `time` | String | Event timestamp |
|
1576
|
+
| `message` | String | Event description |
|
1577
|
+
| `details` | String | Additional event details (JSON string) |
|
1578
|
+
|
1579
|
+
**Instance Methods:**
|
1580
|
+
|
1581
|
+
- `#has_details?` - Check if event has additional details
|
1582
|
+
- `#parsed_details` - Parse details JSON into hash (returns empty hash on parse error)
|
1583
|
+
|
1584
|
+
#### Usage Examples
|
1585
|
+
|
1586
|
+
##### Create a Basic Campaign
|
1587
|
+
|
1588
|
+
```ruby
|
1589
|
+
campaign = Gophish::Campaign.new(
|
1590
|
+
name: "Q1 Security Awareness Training",
|
1591
|
+
template: { name: "Phishing Template" }, # Reference existing template by name
|
1592
|
+
page: { name: "Login Page" }, # Reference existing landing page by name
|
1593
|
+
groups: [{ name: "Marketing Team" }], # Reference existing groups by name
|
1594
|
+
smtp: { name: "Company SMTP" }, # Reference existing SMTP profile by name
|
1595
|
+
url: "https://phishing.company.com" # Base URL for campaign
|
1596
|
+
)
|
1597
|
+
|
1598
|
+
if campaign.save
|
1599
|
+
puts "Campaign created successfully!"
|
1600
|
+
puts " ID: #{campaign.id}"
|
1601
|
+
puts " Name: #{campaign.name}"
|
1602
|
+
puts " Status: #{campaign.status}"
|
1603
|
+
else
|
1604
|
+
puts "Failed to create campaign:"
|
1605
|
+
campaign.errors.full_messages.each { |msg| puts " - #{msg}" }
|
1606
|
+
end
|
1607
|
+
```
|
1608
|
+
|
1609
|
+
##### Create a Scheduled Campaign
|
1610
|
+
|
1611
|
+
```ruby
|
1612
|
+
# Create a campaign with specific launch timing
|
1613
|
+
campaign = Gophish::Campaign.new(
|
1614
|
+
name: "Scheduled Phishing Test",
|
1615
|
+
template: { name: "Email Template" },
|
1616
|
+
page: { name: "Landing Page" },
|
1617
|
+
groups: [
|
1618
|
+
{ name: "HR Department" },
|
1619
|
+
{ name: "Finance Team" }
|
1620
|
+
],
|
1621
|
+
smtp: { name: "SMTP Profile" },
|
1622
|
+
url: "https://training.company.com",
|
1623
|
+
launch_date: "2024-01-15T09:00:00Z", # Start sending at 9 AM
|
1624
|
+
send_by_date: "2024-01-15T17:00:00Z" # Finish sending by 5 PM
|
1625
|
+
)
|
1626
|
+
|
1627
|
+
if campaign.save
|
1628
|
+
puts "Scheduled campaign created:"
|
1629
|
+
puts " Launch Date: #{campaign.launch_date}"
|
1630
|
+
puts " Send By Date: #{campaign.send_by_date}"
|
1631
|
+
puts " Launched? #{campaign.launched?}"
|
1632
|
+
puts " Has send by date? #{campaign.has_send_by_date?}"
|
1633
|
+
end
|
1634
|
+
```
|
1635
|
+
|
1636
|
+
##### Monitor Campaign Progress
|
1637
|
+
|
1638
|
+
```ruby
|
1639
|
+
# Load and monitor an existing campaign
|
1640
|
+
campaign = Gophish::Campaign.find(1)
|
1641
|
+
|
1642
|
+
puts "Campaign: #{campaign.name}"
|
1643
|
+
puts "Status: #{campaign.status}"
|
1644
|
+
puts "In progress? #{campaign.in_progress?}"
|
1645
|
+
puts "Completed? #{campaign.completed?}"
|
1646
|
+
|
1647
|
+
# Get detailed results
|
1648
|
+
results = campaign.get_results
|
1649
|
+
puts "\nCampaign Results Summary:"
|
1650
|
+
puts " Total targets: #{results.length}"
|
1651
|
+
|
1652
|
+
# Analyze results by status
|
1653
|
+
clicked_count = 0
|
1654
|
+
opened_count = 0
|
1655
|
+
reported_count = 0
|
1656
|
+
|
1657
|
+
campaign.results.each do |result|
|
1658
|
+
clicked_count += 1 if result.clicked?
|
1659
|
+
opened_count += 1 if result.opened?
|
1660
|
+
reported_count += 1 if result.reported?
|
1661
|
+
end
|
1662
|
+
|
1663
|
+
puts " Clicked links: #{clicked_count}"
|
1664
|
+
puts " Opened emails: #{opened_count}"
|
1665
|
+
puts " Reported phishing: #{reported_count}"
|
1666
|
+
puts " Click rate: #{(clicked_count.to_f / results.length * 100).round(2)}%"
|
1667
|
+
```
|
1668
|
+
|
1669
|
+
##### Analyze Campaign Timeline
|
1670
|
+
|
1671
|
+
```ruby
|
1672
|
+
campaign = Gophish::Campaign.find(1)
|
1673
|
+
|
1674
|
+
puts "Campaign Timeline:"
|
1675
|
+
campaign.timeline.each do |event|
|
1676
|
+
puts " #{event.time}: #{event.message}"
|
1677
|
+
puts " Email: #{event.email}" if event.email
|
1678
|
+
|
1679
|
+
# Check for additional event details
|
1680
|
+
if event.has_details?
|
1681
|
+
details = event.parsed_details
|
1682
|
+
puts " Details: #{details.inspect}"
|
1683
|
+
end
|
1684
|
+
puts ""
|
1685
|
+
end
|
1686
|
+
```
|
1687
|
+
|
1688
|
+
##### Complete a Running Campaign
|
1689
|
+
|
1690
|
+
```ruby
|
1691
|
+
campaign = Gophish::Campaign.find(1)
|
1692
|
+
|
1693
|
+
if campaign.in_progress?
|
1694
|
+
puts "Completing campaign '#{campaign.name}'..."
|
1695
|
+
|
1696
|
+
result = campaign.complete!
|
1697
|
+
|
1698
|
+
if result['success']
|
1699
|
+
puts "✓ Campaign completed successfully"
|
1700
|
+
puts " Final status: #{campaign.status}"
|
1701
|
+
puts " Completed date: #{campaign.completed_date}"
|
1702
|
+
else
|
1703
|
+
puts "✗ Failed to complete campaign: #{result['message']}"
|
1704
|
+
end
|
1705
|
+
else
|
1706
|
+
puts "Campaign is not in progress (current status: #{campaign.status})"
|
1707
|
+
end
|
1708
|
+
```
|
1709
|
+
|
1710
|
+
##### Campaign with Object References
|
1711
|
+
|
1712
|
+
```ruby
|
1713
|
+
# Create campaign using actual object instances instead of name references
|
1714
|
+
template = Gophish::Template.find(1)
|
1715
|
+
page = Gophish::Page.find(2)
|
1716
|
+
group = Gophish::Group.find(3)
|
1717
|
+
smtp = Gophish::Smtp.find(4)
|
1718
|
+
|
1719
|
+
campaign = Gophish::Campaign.new(
|
1720
|
+
name: "Advanced Campaign Setup",
|
1721
|
+
template: template, # Full template object
|
1722
|
+
page: page, # Full page object
|
1723
|
+
groups: [group], # Array of group objects
|
1724
|
+
smtp: smtp, # Full SMTP object
|
1725
|
+
url: "https://secure.company.com/phishing"
|
1726
|
+
)
|
1727
|
+
|
1728
|
+
# The campaign will automatically serialize these objects appropriately
|
1729
|
+
if campaign.save
|
1730
|
+
puts "Campaign created using object references"
|
1731
|
+
puts "Template: #{campaign.template.name}"
|
1732
|
+
puts "Page: #{campaign.page.name}"
|
1733
|
+
puts "Groups: #{campaign.groups.map(&:name).join(', ')}"
|
1734
|
+
puts "SMTP: #{campaign.smtp.name}"
|
1735
|
+
end
|
1736
|
+
```
|
1737
|
+
|
1738
|
+
##### Campaign Validation Examples
|
1739
|
+
|
1740
|
+
```ruby
|
1741
|
+
# Invalid campaign - missing required components
|
1742
|
+
incomplete_campaign = Gophish::Campaign.new(name: "Incomplete Campaign")
|
1743
|
+
|
1744
|
+
unless incomplete_campaign.valid?
|
1745
|
+
puts "Validation errors:"
|
1746
|
+
incomplete_campaign.errors.full_messages.each { |msg| puts " - #{msg}" }
|
1747
|
+
# Output:
|
1748
|
+
# - Template can't be blank
|
1749
|
+
# - Page can't be blank
|
1750
|
+
# - Groups can't be blank
|
1751
|
+
# - Smtp can't be blank
|
1752
|
+
# - Url can't be blank
|
1753
|
+
end
|
1754
|
+
|
1755
|
+
# Invalid group structure
|
1756
|
+
invalid_groups_campaign = Gophish::Campaign.new(
|
1757
|
+
name: "Test Campaign",
|
1758
|
+
template: { name: "Template" },
|
1759
|
+
page: { name: "Page" },
|
1760
|
+
groups: [{}], # Invalid: group without name
|
1761
|
+
smtp: { name: "SMTP" },
|
1762
|
+
url: "https://test.com"
|
1763
|
+
)
|
1764
|
+
|
1765
|
+
unless invalid_groups_campaign.valid?
|
1766
|
+
puts "Group validation error:"
|
1767
|
+
puts invalid_groups_campaign.errors.full_messages.first
|
1768
|
+
# => "Groups item at index 0 must have a name"
|
1769
|
+
end
|
1770
|
+
|
1771
|
+
# Valid campaign
|
1772
|
+
valid_campaign = Gophish::Campaign.new(
|
1773
|
+
name: "Valid Campaign",
|
1774
|
+
template: { name: "Test Template" },
|
1775
|
+
page: { name: "Test Page" },
|
1776
|
+
groups: [{ name: "Test Group" }],
|
1777
|
+
smtp: { name: "Test SMTP" },
|
1778
|
+
url: "https://valid.example.com"
|
1779
|
+
)
|
1780
|
+
|
1781
|
+
puts "Campaign valid? #{valid_campaign.valid?}" # => true
|
1782
|
+
```
|
1783
|
+
|
1784
|
+
##### Bulk Campaign Operations
|
1785
|
+
|
1786
|
+
```ruby
|
1787
|
+
# Create multiple campaigns programmatically
|
1788
|
+
templates = Gophish::Template.all
|
1789
|
+
groups = Gophish::Group.all
|
1790
|
+
page = Gophish::Page.find(1)
|
1791
|
+
smtp = Gophish::Smtp.find(1)
|
1792
|
+
|
1793
|
+
campaigns_created = 0
|
1794
|
+
|
1795
|
+
templates.each do |template|
|
1796
|
+
campaign = Gophish::Campaign.new(
|
1797
|
+
name: "Auto Campaign - #{template.name}",
|
1798
|
+
template: template,
|
1799
|
+
page: page,
|
1800
|
+
groups: [groups.sample], # Random group for testing
|
1801
|
+
smtp: smtp,
|
1802
|
+
url: "https://training.company.com",
|
1803
|
+
launch_date: (Time.now + 1.hour).iso8601
|
1804
|
+
)
|
1805
|
+
|
1806
|
+
if campaign.save
|
1807
|
+
campaigns_created += 1
|
1808
|
+
puts "✓ Created campaign: #{campaign.name}"
|
1809
|
+
else
|
1810
|
+
puts "✗ Failed to create campaign for template #{template.name}:"
|
1811
|
+
campaign.errors.full_messages.each { |msg| puts " #{msg}" }
|
1812
|
+
end
|
1813
|
+
end
|
1814
|
+
|
1815
|
+
puts "\nCreated #{campaigns_created} campaigns successfully"
|
1816
|
+
```
|
1817
|
+
|
1818
|
+
##### Campaign Reporting
|
1819
|
+
|
1820
|
+
```ruby
|
1821
|
+
# Generate comprehensive campaign report
|
1822
|
+
def generate_campaign_report(campaign_id)
|
1823
|
+
campaign = Gophish::Campaign.find(campaign_id)
|
1824
|
+
|
1825
|
+
puts "="*60
|
1826
|
+
puts "CAMPAIGN REPORT: #{campaign.name}"
|
1827
|
+
puts "="*60
|
1828
|
+
|
1829
|
+
puts "\nBasic Information:"
|
1830
|
+
puts " ID: #{campaign.id}"
|
1831
|
+
puts " Status: #{campaign.status}"
|
1832
|
+
puts " Created: #{campaign.created_date}"
|
1833
|
+
puts " Launched: #{campaign.launch_date || 'Not launched'}"
|
1834
|
+
puts " Completed: #{campaign.completed_date || 'Not completed'}"
|
1835
|
+
|
1836
|
+
puts "\nCampaign Components:"
|
1837
|
+
puts " Template: #{campaign.template.name rescue 'Unknown'}"
|
1838
|
+
puts " Landing Page: #{campaign.page.name rescue 'Unknown'}"
|
1839
|
+
puts " SMTP Profile: #{campaign.smtp.name rescue 'Unknown'}"
|
1840
|
+
puts " Target Groups: #{campaign.groups.map(&:name).join(', ') rescue 'Unknown'}"
|
1841
|
+
|
1842
|
+
puts "\nResults Summary:"
|
1843
|
+
total_targets = campaign.results.length
|
1844
|
+
puts " Total Targets: #{total_targets}"
|
1845
|
+
|
1846
|
+
if total_targets > 0
|
1847
|
+
sent = campaign.results.count(&:sent?)
|
1848
|
+
opened = campaign.results.count(&:opened?)
|
1849
|
+
clicked = campaign.results.count(&:clicked?)
|
1850
|
+
submitted = campaign.results.count(&:submitted_data?)
|
1851
|
+
reported = campaign.results.count(&:reported?)
|
1852
|
+
|
1853
|
+
puts " Emails Sent: #{sent} (#{(sent.to_f/total_targets*100).round(1)}%)"
|
1854
|
+
puts " Emails Opened: #{opened} (#{(opened.to_f/total_targets*100).round(1)}%)"
|
1855
|
+
puts " Links Clicked: #{clicked} (#{(clicked.to_f/total_targets*100).round(1)}%)"
|
1856
|
+
puts " Data Submitted: #{submitted} (#{(submitted.to_f/total_targets*100).round(1)}%)"
|
1857
|
+
puts " Phishing Reported: #{reported} (#{(reported.to_f/total_targets*100).round(1)}%)"
|
1858
|
+
end
|
1859
|
+
|
1860
|
+
puts "\nTimeline Events: #{campaign.timeline.length}"
|
1861
|
+
campaign.timeline.last(5).each do |event|
|
1862
|
+
puts " #{event.time}: #{event.message}"
|
1863
|
+
end
|
1864
|
+
|
1865
|
+
puts "\n" + "="*60
|
1866
|
+
end
|
1867
|
+
|
1868
|
+
# Usage
|
1869
|
+
generate_campaign_report(1)
|
1870
|
+
```
|
1871
|
+
|
947
1872
|
## Error Handling
|
948
1873
|
|
949
1874
|
### Validation Errors
|