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.
- checksums.yaml +7 -0
- data/API.md +142 -0
- data/ARCHITECTURE.md +134 -0
- data/AUTHORS +106 -0
- data/CODE_OF_CONDUCT.md +128 -0
- data/LICENSE +21 -0
- data/README.md +1290 -0
- data/SECURITY.md +67 -0
- data/bin/contractful +128 -0
- data/history.md +801 -0
- data/lib/contractful/abstract_response.rb +301 -0
- data/lib/contractful/circuit_breaker.rb +221 -0
- data/lib/contractful/connection_pool.rb +166 -0
- data/lib/contractful/contract/auth.rb +257 -0
- data/lib/contractful/contract/config.rb +93 -0
- data/lib/contractful/contract/definition.rb +295 -0
- data/lib/contractful/contract/dsl.rb +501 -0
- data/lib/contractful/contract/executor.rb +492 -0
- data/lib/contractful/contract/openapi_import.rb +1135 -0
- data/lib/contractful/contract/pagination.rb +218 -0
- data/lib/contractful/contract/param.rb +505 -0
- data/lib/contractful/contract/postman_import/auth.rb +39 -0
- data/lib/contractful/contract/postman_import/body.rb +87 -0
- data/lib/contractful/contract/postman_import/operation.rb +107 -0
- data/lib/contractful/contract/postman_import/scope.rb +37 -0
- data/lib/contractful/contract/postman_import/url.rb +80 -0
- data/lib/contractful/contract/postman_import.rb +164 -0
- data/lib/contractful/contract/response_field.rb +167 -0
- data/lib/contractful/contract/sequence.rb +432 -0
- data/lib/contractful/contract/swagger2/components.rb +84 -0
- data/lib/contractful/contract/swagger2/operations.rb +81 -0
- data/lib/contractful/contract/swagger2/parameters.rb +93 -0
- data/lib/contractful/contract/swagger2/references.rb +55 -0
- data/lib/contractful/contract/swagger2.rb +55 -0
- data/lib/contractful/contract.rb +85 -0
- data/lib/contractful/exceptions.rb +328 -0
- data/lib/contractful/notifications/active_support.rb +40 -0
- data/lib/contractful/notifications.rb +138 -0
- data/lib/contractful/pacer.rb +161 -0
- data/lib/contractful/params_array.rb +85 -0
- data/lib/contractful/payload.rb +340 -0
- data/lib/contractful/platform.rb +52 -0
- data/lib/contractful/rate_limit.rb +133 -0
- data/lib/contractful/raw_response.rb +70 -0
- data/lib/contractful/request.rb +1483 -0
- data/lib/contractful/resource.rb +189 -0
- data/lib/contractful/response.rb +109 -0
- data/lib/contractful/utils.rb +202 -0
- data/lib/contractful/version.rb +11 -0
- data/lib/contractful/windows/root_certs.rb +107 -0
- data/lib/contractful/windows.rb +9 -0
- data/lib/contractful.rb +233 -0
- 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!
|