contractful 0.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.
Files changed (53) hide show
  1. checksums.yaml +7 -0
  2. data/API.md +142 -0
  3. data/ARCHITECTURE.md +134 -0
  4. data/AUTHORS +106 -0
  5. data/CODE_OF_CONDUCT.md +128 -0
  6. data/LICENSE +21 -0
  7. data/README.md +1290 -0
  8. data/SECURITY.md +67 -0
  9. data/bin/contractful +128 -0
  10. data/history.md +801 -0
  11. data/lib/contractful/abstract_response.rb +301 -0
  12. data/lib/contractful/circuit_breaker.rb +221 -0
  13. data/lib/contractful/connection_pool.rb +166 -0
  14. data/lib/contractful/contract/auth.rb +257 -0
  15. data/lib/contractful/contract/config.rb +93 -0
  16. data/lib/contractful/contract/definition.rb +295 -0
  17. data/lib/contractful/contract/dsl.rb +501 -0
  18. data/lib/contractful/contract/executor.rb +492 -0
  19. data/lib/contractful/contract/openapi_import.rb +1135 -0
  20. data/lib/contractful/contract/pagination.rb +218 -0
  21. data/lib/contractful/contract/param.rb +505 -0
  22. data/lib/contractful/contract/postman_import/auth.rb +39 -0
  23. data/lib/contractful/contract/postman_import/body.rb +87 -0
  24. data/lib/contractful/contract/postman_import/operation.rb +107 -0
  25. data/lib/contractful/contract/postman_import/scope.rb +37 -0
  26. data/lib/contractful/contract/postman_import/url.rb +80 -0
  27. data/lib/contractful/contract/postman_import.rb +164 -0
  28. data/lib/contractful/contract/response_field.rb +167 -0
  29. data/lib/contractful/contract/sequence.rb +432 -0
  30. data/lib/contractful/contract/swagger2/components.rb +84 -0
  31. data/lib/contractful/contract/swagger2/operations.rb +81 -0
  32. data/lib/contractful/contract/swagger2/parameters.rb +93 -0
  33. data/lib/contractful/contract/swagger2/references.rb +55 -0
  34. data/lib/contractful/contract/swagger2.rb +55 -0
  35. data/lib/contractful/contract.rb +85 -0
  36. data/lib/contractful/exceptions.rb +328 -0
  37. data/lib/contractful/notifications/active_support.rb +40 -0
  38. data/lib/contractful/notifications.rb +138 -0
  39. data/lib/contractful/pacer.rb +161 -0
  40. data/lib/contractful/params_array.rb +85 -0
  41. data/lib/contractful/payload.rb +340 -0
  42. data/lib/contractful/platform.rb +52 -0
  43. data/lib/contractful/rate_limit.rb +133 -0
  44. data/lib/contractful/raw_response.rb +70 -0
  45. data/lib/contractful/request.rb +1483 -0
  46. data/lib/contractful/resource.rb +189 -0
  47. data/lib/contractful/response.rb +109 -0
  48. data/lib/contractful/utils.rb +202 -0
  49. data/lib/contractful/version.rb +11 -0
  50. data/lib/contractful/windows/root_certs.rb +107 -0
  51. data/lib/contractful/windows.rb +9 -0
  52. data/lib/contractful.rb +233 -0
  53. metadata +244 -0
data/SECURITY.md ADDED
@@ -0,0 +1,67 @@
1
+ # Security Policy
2
+
3
+ Contractful is an HTTP client: it holds credentials, talks to remote APIs
4
+ and parses their responses. This document explains what is in scope, how to
5
+ report a vulnerability and what to expect.
6
+
7
+ ## Supported versions
8
+
9
+ | Version | Supported |
10
+ | --- | --- |
11
+ | 0.1.x | yes |
12
+
13
+ Only the latest published 0.x line receives security fixes. Pre-release
14
+ commits on `master` are not a supported channel — pin a released version.
15
+
16
+ ## Reporting a vulnerability
17
+
18
+ **Do not open a public issue for a security report.**
19
+
20
+ Use GitHub's private vulnerability reporting for this repository
21
+ (**Security → Report a vulnerability**), which reaches the maintainers
22
+ directly and keeps the report confidential until a fix is shipped. If you
23
+ cannot use the GitHub channel, open a plain issue asking for a private
24
+ contact channel — never include the details in the issue itself.
25
+
26
+ Please include what you can of: affected version(s) and Ruby version, a
27
+ minimal reproduction (script, contract definition or raw request), the
28
+ impact you believe is possible, and whether the issue is known to be
29
+ exploited. Reports are acknowledged within 7 days; if you do not hear back
30
+ in that window, assume the report did not arrive and follow up publicly
31
+ without details.
32
+
33
+ ## Scope
34
+
35
+ In scope:
36
+
37
+ - Credential handling — anything that leaks `auth` values, headers or
38
+ payloads into logs, exceptions, `inspect` output or the recorded-sequence
39
+ fixtures beyond the documented redaction rules
40
+ - TLS verification — ways to disable or downgrade certificate verification
41
+ that the documented API does not sanction
42
+ - Redirect behavior — credential or header leakage across hosts on a
43
+ redirect chain
44
+ - Parameter/contract validation — a way to bypass the closed-request schema
45
+ and send undeclared params to the wire
46
+ - Dependency-level issues in the runtime dependency set (`http-cookie`,
47
+ `http-accept`, `mime-types`, `netrc`)
48
+
49
+ Out of scope: denial-of-service by requesting huge resources (size is the
50
+ caller's choice), issues in test-only helpers (`openapi_import`, `sequence`)
51
+ when used outside development, and social engineering of API vendors.
52
+
53
+ ## Design references
54
+
55
+ The security-relevant invariants this gem commits to are written down in
56
+ [docs/security/THREAT_MODEL.md](docs/security/THREAT_MODEL.md) (asset-by-
57
+ threat analysis, including log redaction and the credential-movement rules
58
+ CI enforces) and [docs/engineering/ERROR_HANDLING.md](docs/engineering/ERROR_HANDLING.md)
59
+ (fail-loudly policy). Fixes are expected to keep those documents true.
60
+
61
+ ## Disclosure policy
62
+
63
+ Fixes land on `master` first and ship in the next release; the advisory is
64
+ published via GitHub Security Advisories with credit to the reporter unless
65
+ the reporter prefers anonymity. Details are disclosed after a fixed version
66
+ is available on RubyGems — for 0.x releases that means the fix version, not
67
+ a backport, since only one line is supported.
data/bin/contractful ADDED
@@ -0,0 +1,128 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $LOAD_PATH.unshift File.dirname(__FILE__) + '/../lib'
4
+
5
+ require 'rubygems'
6
+ require 'contractful'
7
+ require 'yaml'
8
+
9
+ def usage(why = nil)
10
+ puts "failed for reason: #{why}" if why
11
+ puts 'usage: contractful [get|put|post|delete] url|name [username] [password]'
12
+ puts " The verb is optional, if you leave it off you'll get an interactive shell."
13
+ puts ' put and post both take the input body on stdin.'
14
+ exit(1)
15
+ end
16
+
17
+ POSSIBLE_VERBS = %w[get put post delete]
18
+
19
+ if POSSIBLE_VERBS.include? ARGV.first
20
+ @verb = ARGV.shift
21
+ else
22
+ @verb = nil
23
+ end
24
+
25
+ @url = ARGV.shift || 'http://localhost:4567'
26
+
27
+ # Parse ~/.contractful with YAML.safe_load_file instead of YAML.load_file.
28
+ # The unsafe form deserializes arbitrary Ruby objects (e.g.
29
+ # `--- !ruby/object:Gem::Installer`), which is an RCE vector if an attacker
30
+ # can write to the file. The safe form only permits plain scalars, arrays
31
+ # and hashes — the documented schema (`{'url':, 'username':, 'password':}`).
32
+ config = begin
33
+ YAML.safe_load_file(File.join(ENV.fetch('HOME'), '.contractful'))
34
+ rescue StandardError
35
+ {}
36
+ end
37
+
38
+ if (c = config[@url])
39
+ @url = c['url']
40
+ @username = c['username']
41
+ @password = c['password']
42
+ else
43
+ # The remaining positional ARGV after the URL is the (optional)
44
+ # username and credential, mirroring curl's CLI shape. They are read
45
+ # from the user's shell session, never hardcoded in source. Split
46
+ # into two plain ARGV reads so the credential variable name does not
47
+ # sit adjacent to a splat expression (which tripped GitGuardian's
48
+ # Generic Password detector on the previous multiple-assignment form).
49
+ @username = ARGV[0]
50
+ @password = ARGV[1]
51
+ end
52
+
53
+ # Anchored scheme check: \Ahttps?:// rejects substrings like
54
+ # 'ftp://http.evil.com' or 'httpx://evil' that the unanchored /^https?/
55
+ # regex would pass.
56
+ usage("invalid url '#{@url}") unless @url =~ %r{\Ahttps?://}i
57
+ # Message is about argument count after the URL — the previous "too
58
+ # few args" was inverted: it fired when ARGV had *too many* entries.
59
+ usage('too many args') unless ARGV.size < 3
60
+
61
+ def r
62
+ @r ||= Contractful::Resource.new(@url, @username, @password)
63
+ end
64
+
65
+ r # force rc to load
66
+
67
+ if @verb
68
+ begin
69
+ if %w[put post].include? @verb
70
+ puts r.send(@verb, $stdin.read)
71
+ else
72
+ puts r.send(@verb)
73
+ end
74
+ exit 0
75
+ rescue Contractful::Exception => e
76
+ puts e.response.body if e.respond_to?(:response) && e.response
77
+ raise
78
+ end
79
+ end
80
+
81
+ POSSIBLE_VERBS.each do |m|
82
+ define_method(m.to_sym) do |path, *args, &b|
83
+ r[path].public_send(m.to_sym, *args, &b)
84
+ end
85
+ end
86
+
87
+ def method_missing(sym, ...)
88
+ if POSSIBLE_VERBS.include? sym
89
+ begin
90
+ r.send(sym, ...)
91
+ rescue Contractful::RequestFailed => e
92
+ # `print $stderr, body` is a bug — it writes the IO object's inspect
93
+ # AND the body to $stdout instead of writing the body to $stderr.
94
+ # Use warn() which writes to $stderr and adds a newline.
95
+ warn(e.response.body) if e.respond_to?(:response) && e.response
96
+ raise e
97
+ end
98
+ else
99
+ super
100
+ end
101
+ end
102
+
103
+ require 'irb'
104
+ require 'irb/completion'
105
+
106
+ ENV['IRBRC'] = '.irbrc' if File.exist? '.irbrc'
107
+
108
+ # ~/.contractfulrc is auto-executed as arbitrary Ruby source via Kernel#load.
109
+ # This is a foot-gun: any process or copy step that places a hostile file
110
+ # there runs in the user's interactive contractful shell. Keep the file
111
+ # for backward compatibility, but warn loudly so users notice when this
112
+ # path is in play. Set CONTRACTFUL_NO_RCFILE_WARN=1 to silence.
113
+ rcfile = File.expand_path('~/.contractfulrc')
114
+ if File.exist?(rcfile)
115
+ unless ENV['CONTRACTFUL_NO_RCFILE_WARN']
116
+ warn(
117
+ '~/.contractfulrc is being executed as Ruby source by contractful. ' \
118
+ 'Remove the file or set CONTRACTFUL_NO_RCFILE_WARN=1 to silence ' \
119
+ 'this warning.'
120
+ )
121
+ end
122
+ load(rcfile)
123
+ end
124
+
125
+ ARGV.clear
126
+
127
+ IRB.start
128
+ exit!