kwtsms 0.3.0 → 0.3.1
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 +19 -1
- data/README.md +1 -1
- data/lib/kwtsms/client.rb +7 -0
- data/lib/kwtsms/env_loader.rb +1 -0
- data/lib/kwtsms/errors.rb +5 -0
- data/lib/kwtsms/logger.rb +17 -0
- data/lib/kwtsms/phone.rb +8 -4
- data/lib/kwtsms/request.rb +1 -0
- data/lib/kwtsms/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 93dfa6c3d929f5f5767a1dd561078bf3d2e099dfbc8aea04e9d5f0ffa81fbf6a
|
|
4
|
+
data.tar.gz: 4b0c380d47b356060cbc976a1d035c167b03718572f88e2417cbd9f74d5137cd
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 02ef8c6d2c00049be8195aab4b359cbbd0d567c42d77f092ac50d31cb66cd021479b95ad51d46c8fae5205a0bd62b82756f6a6fd11bec40248639974a5dc7a29
|
|
7
|
+
data.tar.gz: 35bf19319f4548a648d96c2802d1176b1e5ed1c2279d4b5d29dcbcedc155b25d94fc50464165aa1c9cf4d6756566b7a0314785efd28c3e6cc36d01445e096976
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,23 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.3.1] - 2026-03-15
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- Password no longer visible in `Client#inspect` or `#to_s` output (redacted)
|
|
13
|
+
- Sanitize raw input in phone validation errors to prevent log injection
|
|
14
|
+
- Command injection risk in dependabot auto-merge workflow (use PR number not URL)
|
|
15
|
+
- Explicit SSL `VERIFY_PEER` on all HTTP connections
|
|
16
|
+
- Log file path traversal protection: rejects `..`, absolute paths, null bytes, pipes
|
|
17
|
+
- `.env` parser now handles `export KEY=VALUE` syntax
|
|
18
|
+
- CI workflow scoped to `permissions: contents: read`
|
|
19
|
+
- Missing error codes ERR019-ERR023 added to `API_ERRORS`
|
|
20
|
+
- Em dashes replaced with commas/colons per style rules
|
|
21
|
+
- SECURITY.md updated to reflect 0.3.x support
|
|
22
|
+
- CONTRIBUTING.md expanded with all required sections
|
|
23
|
+
- Error code test coverage includes ERR019-ERR023
|
|
24
|
+
|
|
8
25
|
## [0.3.0] - 2026-03-15
|
|
9
26
|
|
|
10
27
|
### Added
|
|
@@ -31,7 +48,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
31
48
|
|
|
32
49
|
### Added
|
|
33
50
|
|
|
34
|
-
- Raw API example (`examples/00_raw_api.rb`)
|
|
51
|
+
- Raw API example (`examples/00_raw_api.rb`): call all 7 kwtSMS endpoints using only Ruby stdlib, no gem needed
|
|
35
52
|
- Step-by-step guide (`examples/00_raw_api.md`) with helper function reference, endpoints table, key rules, and going-live checklist
|
|
36
53
|
|
|
37
54
|
### Removed
|
|
@@ -69,6 +86,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
69
86
|
- Comprehensive test suite: unit tests, mocked API tests, and real integration tests
|
|
70
87
|
- Examples: basic usage, OTP flow, bulk SMS, Rails endpoint, error handling, production OTP
|
|
71
88
|
|
|
89
|
+
[0.3.1]: https://github.com/boxlinknet/kwtsms-ruby/releases/tag/v0.3.1
|
|
72
90
|
[0.3.0]: https://github.com/boxlinknet/kwtsms-ruby/releases/tag/v0.3.0
|
|
73
91
|
[0.2.0]: https://github.com/boxlinknet/kwtsms-ruby/releases/tag/v0.2.0
|
|
74
92
|
[0.1.0]: https://github.com/boxlinknet/kwtsms-ruby/releases/tag/v0.1.0
|
data/README.md
CHANGED
|
@@ -344,7 +344,7 @@ See the [examples/](examples/) directory:
|
|
|
344
344
|
|
|
345
345
|
| # | Example | Description |
|
|
346
346
|
|---|---------|-------------|
|
|
347
|
-
| 00 | [Raw API](examples/00_raw_api.rb) | Call all 7 endpoints directly
|
|
347
|
+
| 00 | [Raw API](examples/00_raw_api.rb) | Call all 7 endpoints directly, no gem needed |
|
|
348
348
|
| 01 | [Basic Usage](examples/01_basic_usage.rb) | Connect, verify, send SMS, validate |
|
|
349
349
|
| 02 | [OTP Flow](examples/02_otp_flow.rb) | Send OTP codes |
|
|
350
350
|
| 03 | [Bulk SMS](examples/03_bulk_sms.rb) | Send to many recipients |
|
data/lib/kwtsms/client.rb
CHANGED
|
@@ -43,6 +43,13 @@ module KwtSMS
|
|
|
43
43
|
@cached_purchased = nil
|
|
44
44
|
end
|
|
45
45
|
|
|
46
|
+
# Redact password from inspect/to_s output to prevent accidental exposure
|
|
47
|
+
# in logs, error trackers (Sentry, Bugsnag), or REPL sessions.
|
|
48
|
+
def inspect
|
|
49
|
+
"#<#{self.class} @username=#{@username.inspect} @sender_id=#{@sender_id.inspect} @test_mode=#{@test_mode}>"
|
|
50
|
+
end
|
|
51
|
+
alias_method :to_s, :inspect
|
|
52
|
+
|
|
46
53
|
# Load credentials from environment variables, falling back to .env file.
|
|
47
54
|
#
|
|
48
55
|
# Required env vars:
|
data/lib/kwtsms/env_loader.rb
CHANGED
data/lib/kwtsms/errors.rb
CHANGED
|
@@ -17,6 +17,11 @@ module KwtSMS
|
|
|
17
17
|
"ERR011" => "Insufficient balance for this send. Buy more credits at kwtsms.com.",
|
|
18
18
|
"ERR012" => "Message is too long (over 6 SMS pages). Shorten your message.",
|
|
19
19
|
"ERR013" => "Send queue is full (1000 messages). Wait a moment and try again.",
|
|
20
|
+
"ERR019" => "No delivery reports found for this message.",
|
|
21
|
+
"ERR020" => "Message ID does not exist. Make sure you saved the msg-id from the send response.",
|
|
22
|
+
"ERR021" => "No delivery report available for this message yet.",
|
|
23
|
+
"ERR022" => "Delivery reports are not ready yet. Try again after 24 hours.",
|
|
24
|
+
"ERR023" => "Unknown delivery report error. Contact kwtSMS support.",
|
|
20
25
|
"ERR024" => "Your IP address is not in the API whitelist. Add it at kwtsms.com > Account > API > IP Lockdown, or disable IP lockdown.",
|
|
21
26
|
"ERR025" => "Invalid phone number. Make sure the number includes the country code (e.g., 96598765432 for Kuwait, not 98765432).",
|
|
22
27
|
"ERR026" => "This country is not activated on your account. Contact kwtSMS support to enable the destination country.",
|
data/lib/kwtsms/logger.rb
CHANGED
|
@@ -4,10 +4,27 @@ require "json"
|
|
|
4
4
|
require "time"
|
|
5
5
|
|
|
6
6
|
module KwtSMS
|
|
7
|
+
# Validate a log file path. Returns [valid, error].
|
|
8
|
+
# Rejects path traversal, absolute paths outside CWD, and device paths.
|
|
9
|
+
def self.validate_log_path(log_file)
|
|
10
|
+
return [false, "log_file must be a String"] unless log_file.is_a?(String)
|
|
11
|
+
return [false, "log_file must not be empty"] if log_file.strip.empty?
|
|
12
|
+
return [false, "log_file must not contain '..'"] if log_file.include?("..")
|
|
13
|
+
return [false, "log_file must not contain null bytes"] if log_file.include?("\0")
|
|
14
|
+
return [false, "log_file must not start with /"] if log_file.start_with?("/")
|
|
15
|
+
return [false, "log_file must not start with ~"] if log_file.start_with?("~")
|
|
16
|
+
return [false, "log_file must not be a pipe or device"] if log_file.start_with?("|") || log_file.match?(%r{\A/dev/})
|
|
17
|
+
|
|
18
|
+
[true, nil]
|
|
19
|
+
end
|
|
20
|
+
|
|
7
21
|
# Append a JSONL log entry. Never raises. Logging must not break main flow.
|
|
8
22
|
def self.write_log(log_file, entry)
|
|
9
23
|
return if log_file.nil? || log_file.empty?
|
|
10
24
|
|
|
25
|
+
valid, = validate_log_path(log_file)
|
|
26
|
+
return unless valid
|
|
27
|
+
|
|
11
28
|
begin
|
|
12
29
|
File.open(log_file, "a", encoding: "utf-8") do |f|
|
|
13
30
|
f.puts(JSON.generate(entry))
|
data/lib/kwtsms/phone.rb
CHANGED
|
@@ -222,24 +222,28 @@ module KwtSMS
|
|
|
222
222
|
# 1. Empty / blank
|
|
223
223
|
return [false, "Phone number is required", ""] if raw.empty?
|
|
224
224
|
|
|
225
|
+
# Sanitize raw input for safe interpolation in error messages:
|
|
226
|
+
# strip control chars and truncate to prevent log injection
|
|
227
|
+
safe = raw.gsub(/[[:cntrl:]]/, "")[0, 50]
|
|
228
|
+
|
|
225
229
|
# 2. Email address entered by mistake
|
|
226
|
-
return [false, "'#{
|
|
230
|
+
return [false, "'#{safe}' is an email address, not a phone number", ""] if raw.include?("@")
|
|
227
231
|
|
|
228
232
|
# 3. Normalize
|
|
229
233
|
normalized = normalize_phone(raw)
|
|
230
234
|
|
|
231
235
|
# 4. No digits survived normalization
|
|
232
|
-
return [false, "'#{
|
|
236
|
+
return [false, "'#{safe}' is not a valid phone number, no digits found", ""] if normalized.empty?
|
|
233
237
|
|
|
234
238
|
# 5. Too short
|
|
235
239
|
if normalized.length < 7
|
|
236
240
|
digit_word = normalized.length == 1 ? "digit" : "digits"
|
|
237
|
-
return [false, "'#{
|
|
241
|
+
return [false, "'#{safe}' is too short to be a valid phone number (#{normalized.length} #{digit_word}, minimum is 7)", normalized]
|
|
238
242
|
end
|
|
239
243
|
|
|
240
244
|
# 6. Too long
|
|
241
245
|
if normalized.length > 15
|
|
242
|
-
return [false, "'#{
|
|
246
|
+
return [false, "'#{safe}' is too long to be a valid phone number (#{normalized.length} digits, maximum is 15)", normalized]
|
|
243
247
|
end
|
|
244
248
|
|
|
245
249
|
# 7. Country-specific format validation
|
data/lib/kwtsms/request.rb
CHANGED
data/lib/kwtsms/version.rb
CHANGED