straycall 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/CHANGELOG.md +7 -0
- data/LICENSE.txt +21 -0
- data/README.md +125 -0
- data/Rakefile +8 -0
- data/benchmark/require_overhead.rb +67 -0
- data/docs/index.html +186 -0
- data/docs/styles.css +718 -0
- data/examples/.straycall.yml +13 -0
- data/examples/rspec.rb +13 -0
- data/exe/straycall +6 -0
- data/exe/straycall-supervisor +29 -0
- data/lib/straycall/cli.rb +73 -0
- data/lib/straycall/config.rb +217 -0
- data/lib/straycall/handlers/exec.rb +22 -0
- data/lib/straycall/handlers/filesystem.rb +65 -0
- data/lib/straycall/in_process.rb +74 -0
- data/lib/straycall/minitest.rb +47 -0
- data/lib/straycall/policy/exec.rb +44 -0
- data/lib/straycall/policy/filesystem.rb +119 -0
- data/lib/straycall/policy/network.rb +83 -0
- data/lib/straycall/policy.rb +45 -0
- data/lib/straycall/prompt.rb +24 -0
- data/lib/straycall/recorder.rb +38 -0
- data/lib/straycall/report/console.rb +29 -0
- data/lib/straycall/report/json.rb +19 -0
- data/lib/straycall/reporter/client.rb +102 -0
- data/lib/straycall/reporter/protocol.rb +79 -0
- data/lib/straycall/reporter/symbolizer.rb +26 -0
- data/lib/straycall/reporter/thread_reporter.rb +73 -0
- data/lib/straycall/rspec.rb +51 -0
- data/lib/straycall/supervisor.rb +249 -0
- data/lib/straycall/target.rb +31 -0
- data/lib/straycall/version.rb +5 -0
- data/lib/straycall/violation.rb +9 -0
- data/lib/straycall.rb +39 -0
- metadata +94 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 3148046a174ba55ee9edf9d943c9e834391ec89f16684df60bae3b3bcecbfcf4
|
|
4
|
+
data.tar.gz: ed337c7d5cf746f9384f9ee9d6397aa53926ecd3e770adf162e43996a820bda4
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 7e8833fbe0ec64716d1ca1bf179b2b7572006e0ea4743d351f1813e10d16a9de61e9ca7d192adffab6e239b610c290b86a247db844b82c18b6cc92f5a0b54611
|
|
7
|
+
data.tar.gz: 27df7b94480aaeb6c99742b55a32ad0f212b988fabcea451fbe8d6a1063baad91719060e14efb104c32849a25fa1cf458a42b7a0dd41097232b1b3ed5612eef0
|
data/CHANGELOG.md
ADDED
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Yudai Takada
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# Straycall
|
|
2
|
+
|
|
3
|
+
Straycall reports and blocks unintended network, filesystem, and process access from Ruby builds and test suites. It runs an unfiltered supervisor over Linux `seccomp` user notifications and includes the responsible Ruby backtrace in its report.
|
|
4
|
+
|
|
5
|
+
> [!WARNING]
|
|
6
|
+
> Straycall detects accidents; it is not a security boundary for hostile code. Pointer-based policy checks, symbolic links, existing file descriptors, and kernel interfaces outside seccomp's reach leave intentional escape routes.
|
|
7
|
+
|
|
8
|
+
On macOS, Windows, non-MRI Ruby, an unsupported kernel, or with `STRAYCALL=0`, requiring and configuring Straycall is a no-op. The wrapped command runs normally.
|
|
9
|
+
|
|
10
|
+
## Requirements
|
|
11
|
+
|
|
12
|
+
- Linux x86_64 or aarch64 with seccomp user notifications and `CONTINUE` (Linux 5.5+)
|
|
13
|
+
- MRI Ruby 3.1+
|
|
14
|
+
- `seccomp-notify` 0.2.x
|
|
15
|
+
|
|
16
|
+
Container runtimes may block `seccomp(2)` in their own profile. Use an isolated container with `--security-opt seccomp=unconfined` when needed.
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
```ruby
|
|
21
|
+
gem "straycall"
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## CLI (recommended)
|
|
25
|
+
|
|
26
|
+
The CLI keeps the supervisor outside the filtered command, so it does not interfere with the command's `Process.wait` calls.
|
|
27
|
+
|
|
28
|
+
```sh
|
|
29
|
+
straycall --config .straycall.yml -- bundle exec rspec
|
|
30
|
+
straycall --report-path tmp/straycall.json -- ruby script.rb
|
|
31
|
+
straycall --record --policy-path .straycall.yml -- bundle exec rspec
|
|
32
|
+
straycall --preset=bundle-install -- bundle install
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
`--warn` and `--record` allow the syscall after reporting it. `--prompt` asks through `/dev/tty`; without a TTY it denies. The default `fail` mode and a denied prompt return `EPERM` and make the CLI exit nonzero.
|
|
36
|
+
|
|
37
|
+
## YAML policy
|
|
38
|
+
|
|
39
|
+
```yaml
|
|
40
|
+
network:
|
|
41
|
+
default: deny
|
|
42
|
+
allow:
|
|
43
|
+
- host: 127.0.0.1
|
|
44
|
+
ports: [5432, 6379]
|
|
45
|
+
- unix: /var/run/postgresql/.s.PGSQL.5432
|
|
46
|
+
write:
|
|
47
|
+
default: deny
|
|
48
|
+
allow: [tmp, log, coverage, /tmp]
|
|
49
|
+
deny_read: [/etc/shadow, ~/.ssh, ~/.aws, ~/.gem/credentials]
|
|
50
|
+
exec:
|
|
51
|
+
default: allow
|
|
52
|
+
deny: [/usr/bin/curl, /usr/bin/wget]
|
|
53
|
+
on_violation: fail
|
|
54
|
+
report_backtrace: true
|
|
55
|
+
report_path: tmp/straycall.json
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Hosts are resolved once while loading the configuration. Paths are expanded but symbolic links are deliberately not resolved.
|
|
59
|
+
|
|
60
|
+
## Ruby configuration
|
|
61
|
+
|
|
62
|
+
```ruby
|
|
63
|
+
require "straycall"
|
|
64
|
+
|
|
65
|
+
Straycall.configure do |config|
|
|
66
|
+
config.deny_network!
|
|
67
|
+
config.allow_loopback ports: [5432, 6379]
|
|
68
|
+
config.allow_host "registry.internal.example", ports: [443]
|
|
69
|
+
config.allow_unix "/var/run/postgresql/.s.PGSQL.5432"
|
|
70
|
+
|
|
71
|
+
config.allow_write_under "tmp", "log", "coverage", Dir.tmpdir
|
|
72
|
+
config.deny_write_elsewhere!
|
|
73
|
+
config.deny_read "/etc/shadow", "~/.ssh"
|
|
74
|
+
|
|
75
|
+
config.allow_exec "/usr/bin/git", "/usr/bin/make"
|
|
76
|
+
config.deny_exec_elsewhere!
|
|
77
|
+
# config.allow_io_uring! # disables complete syscall coverage
|
|
78
|
+
config.report_path = "tmp/straycall.json"
|
|
79
|
+
end
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## RSpec and Minitest
|
|
83
|
+
|
|
84
|
+
Load the integration at the top of the test helper:
|
|
85
|
+
|
|
86
|
+
```ruby
|
|
87
|
+
# RSpec
|
|
88
|
+
require "straycall/rspec"
|
|
89
|
+
|
|
90
|
+
# Minitest
|
|
91
|
+
require "straycall/minitest"
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
This in-process mode starts a clean detached supervisor with `Process.spawn`, associates violations with the current test, and checks the supervisor health pipe. Prefer the CLI if tests use broad `Process.wait` calls or fork heavily; filters are inherited and cannot be removed.
|
|
95
|
+
|
|
96
|
+
## Reports
|
|
97
|
+
|
|
98
|
+
Console output includes the syscall, target, current test, Ruby backtrace (MRI), a `/proc/<tid>/maps` native-library fallback, and an allow-list suggestion. `report_path` writes a versioned JSON document:
|
|
99
|
+
|
|
100
|
+
```json
|
|
101
|
+
{"version":1,"violations":[{"syscall":"connect","tid":123,"target":{"type":"inet","host":"192.0.2.1","port":443},"action":"denied"}]}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Known limitations
|
|
105
|
+
|
|
106
|
+
1. This is not a security boundary. Allowing a pointer-based syscall with `continue!` is subject to TOCTOU.
|
|
107
|
+
2. vDSO calls such as normal clock reads cannot be intercepted.
|
|
108
|
+
3. File descriptors opened before filter installation are outside the policy.
|
|
109
|
+
4. `io_uring_setup` is denied so io_uring cannot bypass monitored syscalls. `allow_io_uring!` opts out, but network and filesystem coverage is then incomplete.
|
|
110
|
+
5. Ruby backtraces require MRI 3.1+; native calls fall back to `/proc/<tid>/maps`.
|
|
111
|
+
6. Filesystem checks use the unresolved path string. A symlink can point outside an allowed root.
|
|
112
|
+
7. If the supervisor dies, the kernel returns `ENOSYS`; integrations detect the health-pipe closure as soon as they regain control.
|
|
113
|
+
8. A connected `sendto`/`sendmsg`/`sendmmsg` without an explicit destination relies on the earlier monitored `connect`; Straycall cannot recover a peer address from the target file descriptor.
|
|
114
|
+
9. Listener tracking observes an allowed `bind` before the kernel returns. It prevents accidental auto-bind listeners, but it is not a hostile-code boundary.
|
|
115
|
+
|
|
116
|
+
## Development
|
|
117
|
+
|
|
118
|
+
```sh
|
|
119
|
+
ruby -Ilib -S rspec
|
|
120
|
+
gem build straycall.gemspec
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Real seccomp integration examples skip automatically outside supported Linux environments.
|
|
124
|
+
|
|
125
|
+
On Linux, `ruby benchmark/require_overhead.rb` checks that read-only `openat` overhead stays below 10% across 100,000 generated require files, using the median ratio of five alternating pairs after warmup. Set `STRAYCALL_BENCHMARK_FILES` to scale the workload.
|
data/Rakefile
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "benchmark"
|
|
4
|
+
require "rbconfig"
|
|
5
|
+
require "tmpdir"
|
|
6
|
+
|
|
7
|
+
abort "straycall is unavailable" unless RUBY_PLATFORM.include?("linux")
|
|
8
|
+
|
|
9
|
+
Dir.mktmpdir do |directory|
|
|
10
|
+
files = File.join(directory, "files")
|
|
11
|
+
Dir.mkdir(files)
|
|
12
|
+
count = Integer(ENV.fetch("STRAYCALL_BENCHMARK_FILES", "100000"))
|
|
13
|
+
source = <<~RUBY
|
|
14
|
+
# frozen_string_literal: true
|
|
15
|
+
|
|
16
|
+
module StraycallRequireBenchmark
|
|
17
|
+
def self.loaded?
|
|
18
|
+
true
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def self.call(value)
|
|
22
|
+
value
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
RUBY
|
|
26
|
+
count.times { |index| File.write(File.join(files, "f#{index}.rb"), source) }
|
|
27
|
+
script = "Dir[#{files.inspect} + '/*.rb'].each { |file| require file }"
|
|
28
|
+
config = File.join(directory, "config.yml")
|
|
29
|
+
File.write(config, <<~YAML)
|
|
30
|
+
network: {default: deny}
|
|
31
|
+
write:
|
|
32
|
+
default: deny
|
|
33
|
+
allow: [#{directory}]
|
|
34
|
+
YAML
|
|
35
|
+
|
|
36
|
+
clean_environment = ENV.keys.grep(/\A(?:BUNDLE|BUNDLER|RUBY(?:OPT|LIB))/).to_h { |name| [name, nil] }
|
|
37
|
+
plain_command = [RbConfig.ruby, "-e", script]
|
|
38
|
+
guarded_command = [
|
|
39
|
+
RbConfig.ruby,
|
|
40
|
+
"-I#{File.expand_path("../lib", __dir__)}",
|
|
41
|
+
"-I#{File.expand_path("../../seccomp-notify/lib", __dir__)}",
|
|
42
|
+
File.expand_path("../exe/straycall", __dir__),
|
|
43
|
+
"--config", config, "--", RbConfig.ruby, "-e", script
|
|
44
|
+
]
|
|
45
|
+
measure = lambda do |command|
|
|
46
|
+
Benchmark.realtime { system(clean_environment, *command, out: File::NULL, exception: true) }
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
measure.call(plain_command)
|
|
50
|
+
measure.call(guarded_command)
|
|
51
|
+
samples = {plain_command => [], guarded_command => []}
|
|
52
|
+
ratios = []
|
|
53
|
+
5.times do |index|
|
|
54
|
+
first, second = index.even? ? [plain_command, guarded_command] : [guarded_command, plain_command]
|
|
55
|
+
first_time = measure.call(first)
|
|
56
|
+
second_time = measure.call(second)
|
|
57
|
+
samples.fetch(first) << first_time
|
|
58
|
+
samples.fetch(second) << second_time
|
|
59
|
+
timings = {first => first_time, second => second_time}
|
|
60
|
+
ratios << timings.fetch(guarded_command) / timings.fetch(plain_command)
|
|
61
|
+
end
|
|
62
|
+
plain = samples.fetch(plain_command).sort.fetch(2)
|
|
63
|
+
guarded = samples.fetch(guarded_command).sort.fetch(2)
|
|
64
|
+
overhead = (ratios.sort.fetch(2) - 1) * 100
|
|
65
|
+
puts format("files=%d plain=%.3fs guarded=%.3fs overhead=%.1f%%", count, plain, guarded, overhead)
|
|
66
|
+
abort "overhead exceeded 10%" if overhead > 10
|
|
67
|
+
end
|
data/docs/index.html
ADDED
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
+
<meta name="description" content="Straycall reports and blocks unintended network, filesystem, and process access from Ruby builds and test suites.">
|
|
7
|
+
<meta name="theme-color" content="#07111b">
|
|
8
|
+
<meta property="og:title" content="Straycall — Catch unintended syscalls in Ruby tests">
|
|
9
|
+
<meta property="og:description" content="Report and block unintended network, filesystem, and process access from Ruby builds and test suites.">
|
|
10
|
+
<meta property="og:type" content="website">
|
|
11
|
+
<meta property="og:url" content="https://ydah.github.io/straycall/">
|
|
12
|
+
<title>Straycall — Syscall guard for Ruby tests</title>
|
|
13
|
+
<link rel="canonical" href="https://ydah.github.io/straycall/">
|
|
14
|
+
<link rel="stylesheet" href="styles.css">
|
|
15
|
+
</head>
|
|
16
|
+
<body id="top">
|
|
17
|
+
<a class="skip-link" href="#main">Skip to content</a>
|
|
18
|
+
|
|
19
|
+
<header class="site-header">
|
|
20
|
+
<a class="brand" href="#top" aria-label="Straycall home">
|
|
21
|
+
<span class="brand-mark" aria-hidden="true">S/</span>
|
|
22
|
+
<span>Straycall</span>
|
|
23
|
+
</a>
|
|
24
|
+
<nav aria-label="Primary navigation">
|
|
25
|
+
<a href="#features">Guardrails</a>
|
|
26
|
+
<a href="#quick-start">Quick start</a>
|
|
27
|
+
<a href="#policy">Policy</a>
|
|
28
|
+
<a class="nav-cta" href="https://github.com/ydah/straycall">View on GitHub</a>
|
|
29
|
+
</nav>
|
|
30
|
+
</header>
|
|
31
|
+
|
|
32
|
+
<main id="main">
|
|
33
|
+
<section class="hero" aria-labelledby="hero-title">
|
|
34
|
+
<div class="hero-copy">
|
|
35
|
+
<p class="eyebrow">Linux syscall guard for Ruby</p>
|
|
36
|
+
<h1 id="hero-title">Catch the calls your tests <em>didn't mean to make.</em></h1>
|
|
37
|
+
<p class="hero-lede">
|
|
38
|
+
Straycall reports and blocks unintended network, filesystem, and process access from Ruby builds and test suites—then points to the responsible Ruby code.
|
|
39
|
+
</p>
|
|
40
|
+
<div class="hero-actions">
|
|
41
|
+
<a class="button button-primary" href="#quick-start">Get started</a>
|
|
42
|
+
<a class="button button-secondary" href="https://rubygems.org/gems/straycall">View on RubyGems</a>
|
|
43
|
+
</div>
|
|
44
|
+
<ul class="support-list" aria-label="Platform support">
|
|
45
|
+
<li>Linux 5.5+</li>
|
|
46
|
+
<li>MRI Ruby 3.1+</li>
|
|
47
|
+
<li>x86_64 & arm64</li>
|
|
48
|
+
</ul>
|
|
49
|
+
</div>
|
|
50
|
+
|
|
51
|
+
<div class="terminal" aria-label="Example Straycall violation report">
|
|
52
|
+
<div class="terminal-bar" aria-hidden="true">
|
|
53
|
+
<p>bundle exec rspec</p>
|
|
54
|
+
</div>
|
|
55
|
+
<pre><code><span class="prompt">$</span> straycall -- bundle exec rspec
|
|
56
|
+
|
|
57
|
+
<span class="accent">straycall:</span> connect(2) → 192.0.2.1:443
|
|
58
|
+
spec/services/sync_spec.rb:27:in `fetch'
|
|
59
|
+
app/services/sync.rb:14:in `call'
|
|
60
|
+
|
|
61
|
+
<span class="hint">hint:</span> allow_host "192.0.2.1", ports: [443]
|
|
62
|
+
|
|
63
|
+
<span class="failure">1 example, 1 syscall violation</span></code></pre>
|
|
64
|
+
</div>
|
|
65
|
+
</section>
|
|
66
|
+
|
|
67
|
+
<section class="section" id="features" aria-labelledby="features-title">
|
|
68
|
+
<div class="section-heading">
|
|
69
|
+
<p class="kicker">Guardrails</p>
|
|
70
|
+
<h2 id="features-title">Three boundaries. One useful report.</h2>
|
|
71
|
+
<p>Keep accidental side effects out of builds and tests without losing the context needed to fix them.</p>
|
|
72
|
+
</div>
|
|
73
|
+
<div class="feature-grid">
|
|
74
|
+
<article class="feature-card">
|
|
75
|
+
<span class="feature-index" aria-hidden="true">01</span>
|
|
76
|
+
<h3>Network</h3>
|
|
77
|
+
<p>Control outbound connections, Unix sockets, binds, and listeners by host and port.</p>
|
|
78
|
+
<code>deny_network!</code>
|
|
79
|
+
</article>
|
|
80
|
+
<article class="feature-card">
|
|
81
|
+
<span class="feature-index" aria-hidden="true">02</span>
|
|
82
|
+
<h3>Filesystem</h3>
|
|
83
|
+
<p>Allow writes only below known roots and report reads of sensitive paths.</p>
|
|
84
|
+
<code>deny_write_elsewhere!</code>
|
|
85
|
+
</article>
|
|
86
|
+
<article class="feature-card">
|
|
87
|
+
<span class="feature-index" aria-hidden="true">03</span>
|
|
88
|
+
<h3>Processes</h3>
|
|
89
|
+
<p>Restrict executables to an explicit allow list and see where each launch began.</p>
|
|
90
|
+
<code>deny_exec_elsewhere!</code>
|
|
91
|
+
</article>
|
|
92
|
+
</div>
|
|
93
|
+
</section>
|
|
94
|
+
|
|
95
|
+
<section class="section split" id="quick-start" aria-labelledby="quick-start-title">
|
|
96
|
+
<div class="section-heading">
|
|
97
|
+
<p class="kicker">Quick start</p>
|
|
98
|
+
<h2 id="quick-start-title">Install. Wrap. Inspect.</h2>
|
|
99
|
+
<p>The CLI keeps the supervisor outside the filtered command, so it stays out of your test runner's process handling.</p>
|
|
100
|
+
</div>
|
|
101
|
+
<ol class="steps">
|
|
102
|
+
<li>
|
|
103
|
+
<div class="step-heading"><span>1</span><h3>Add the gem</h3></div>
|
|
104
|
+
<pre><code><span class="muted"># Gemfile</span>
|
|
105
|
+
gem "straycall"</code></pre>
|
|
106
|
+
</li>
|
|
107
|
+
<li>
|
|
108
|
+
<div class="step-heading"><span>2</span><h3>Wrap a command</h3></div>
|
|
109
|
+
<pre><code>straycall --config .straycall.yml -- bundle exec rspec</code></pre>
|
|
110
|
+
</li>
|
|
111
|
+
<li>
|
|
112
|
+
<div class="step-heading"><span>3</span><h3>Choose the response</h3></div>
|
|
113
|
+
<div class="mode-list">
|
|
114
|
+
<p><code>fail</code><span>Block and exit nonzero</span></p>
|
|
115
|
+
<p><code>warn</code><span>Allow and report</span></p>
|
|
116
|
+
<p><code>record</code><span>Generate a reviewable policy</span></p>
|
|
117
|
+
<p><code>prompt</code><span>Ask through the terminal</span></p>
|
|
118
|
+
</div>
|
|
119
|
+
</li>
|
|
120
|
+
</ol>
|
|
121
|
+
</section>
|
|
122
|
+
|
|
123
|
+
<section class="section policy-section" id="policy" aria-labelledby="policy-title">
|
|
124
|
+
<div class="policy-copy">
|
|
125
|
+
<p class="kicker">Policy as code</p>
|
|
126
|
+
<h2 id="policy-title">Specific enough to trust.<br>Small enough to review.</h2>
|
|
127
|
+
<p>Keep the policy beside the project. Hosts resolve once at load time; paths expand without silently resolving symbolic links.</p>
|
|
128
|
+
<a class="text-link" href="https://github.com/ydah/straycall#yaml-policy">Read the configuration reference <span aria-hidden="true">→</span></a>
|
|
129
|
+
</div>
|
|
130
|
+
<div class="code-panel">
|
|
131
|
+
<p>.straycall.yml</p>
|
|
132
|
+
<pre><code><span class="key">network:</span>
|
|
133
|
+
<span class="key">default:</span> deny
|
|
134
|
+
<span class="key">allow:</span>
|
|
135
|
+
- <span class="key">host:</span> 127.0.0.1
|
|
136
|
+
<span class="key">ports:</span> [5432, 6379]
|
|
137
|
+
|
|
138
|
+
<span class="key">write:</span>
|
|
139
|
+
<span class="key">default:</span> deny
|
|
140
|
+
<span class="key">allow:</span> [tmp, log, coverage]
|
|
141
|
+
<span class="key">deny_read:</span> [~/.ssh, ~/.aws]
|
|
142
|
+
|
|
143
|
+
<span class="key">exec:</span>
|
|
144
|
+
<span class="key">default:</span> allow
|
|
145
|
+
<span class="key">deny:</span> [/usr/bin/curl]
|
|
146
|
+
|
|
147
|
+
<span class="key">on_violation:</span> fail</code></pre>
|
|
148
|
+
</div>
|
|
149
|
+
</section>
|
|
150
|
+
|
|
151
|
+
<section class="section" aria-labelledby="how-title">
|
|
152
|
+
<div class="section-heading">
|
|
153
|
+
<p class="kicker">How it works</p>
|
|
154
|
+
<h2 id="how-title">Outside the process. Close to the call.</h2>
|
|
155
|
+
</div>
|
|
156
|
+
<div class="flow" role="list">
|
|
157
|
+
<div role="listitem"><span>01</span><h3>Observe</h3><p>Linux pauses selected syscalls through seccomp user notifications.</p></div>
|
|
158
|
+
<div role="listitem"><span>02</span><h3>Decide</h3><p>An unfiltered supervisor evaluates the target against your policy.</p></div>
|
|
159
|
+
<div role="listitem"><span>03</span><h3>Explain</h3><p>The report joins the syscall target with the responsible Ruby backtrace.</p></div>
|
|
160
|
+
</div>
|
|
161
|
+
</section>
|
|
162
|
+
|
|
163
|
+
<aside class="boundary" aria-labelledby="boundary-title">
|
|
164
|
+
<p class="kicker">An honest boundary</p>
|
|
165
|
+
<h2 id="boundary-title">Built to catch accidents—not hostile code.</h2>
|
|
166
|
+
<p>Pointer-based checks, symbolic links, existing file descriptors, and kernel interfaces outside seccomp's reach leave intentional escape routes. Use Straycall as a test guardrail, not a security sandbox.</p>
|
|
167
|
+
</aside>
|
|
168
|
+
|
|
169
|
+
<section class="cta" aria-labelledby="cta-title">
|
|
170
|
+
<p class="kicker">Ready to listen?</p>
|
|
171
|
+
<h2 id="cta-title">Make unintended calls visible.</h2>
|
|
172
|
+
<p>Install Straycall and wrap your next Ruby build or test run.</p>
|
|
173
|
+
<div class="hero-actions">
|
|
174
|
+
<a class="button button-primary" href="https://rubygems.org/gems/straycall">Install from RubyGems</a>
|
|
175
|
+
<a class="button button-secondary" href="https://github.com/ydah/straycall">Explore the source</a>
|
|
176
|
+
</div>
|
|
177
|
+
</section>
|
|
178
|
+
</main>
|
|
179
|
+
|
|
180
|
+
<footer>
|
|
181
|
+
<a class="brand" href="#top"><span class="brand-mark" aria-hidden="true">S/</span><span>Straycall</span></a>
|
|
182
|
+
<p>Open source under the MIT License.</p>
|
|
183
|
+
<div><a href="https://github.com/ydah/straycall">GitHub</a><a href="https://rubygems.org/gems/straycall">RubyGems</a></div>
|
|
184
|
+
</footer>
|
|
185
|
+
</body>
|
|
186
|
+
</html>
|