Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ cargo install httpjail
- 🌐 **HTTP/HTTPS interception** - Transparent proxy with TLS certificate injection
- 🎯 **Regex-based filtering** - Flexible allow/deny rules with regex patterns
- 📝 **Request logging** - Monitor and log all HTTP/HTTPS requests
- ⛔ **Default deny** - Requests are blocked unless explicitly allowed
- 🖥️ **Cross-platform** - Native support for Linux and macOS
- ⚡ **Zero configuration** - Works out of the box with sensible defaults

Expand All @@ -28,6 +29,8 @@ cargo install httpjail

## Quick Start

> By default, httpjail denies all network requests. Add `allow:` rules to permit traffic.

```bash
# Allow only requests to github.com
httpjail -r "allow: github\.com" -r "deny: .*" -- claude
Expand Down
49 changes: 19 additions & 30 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -194,10 +194,9 @@ fn build_rules(args: &Args) -> Result<Vec<Rule>> {
rules.push(parse_rule(rule_str)?);
}

// If no rules specified, default to allow all (for testing)
// If no rules specified, the rule engine will deny all requests by default
if rules.is_empty() {
info!("No rules specified, defaulting to allow all");
rules.push(Rule::new(Action::Allow, ".*")?);
info!("No rules specified; unmatched requests will be denied");
}

Ok(rules)
Expand Down Expand Up @@ -524,36 +523,26 @@ mod tests {
use hyper::Method;

#[test]
fn test_rule_matching() {
let rule = Rule::new(Action::Allow, r"github\.com").unwrap();
assert!(rule.matches(Method::GET, "https://github.com/user/repo"));
assert!(rule.matches(Method::POST, "http://api.github.com/v3/repos"));
assert!(!rule.matches(Method::GET, "https://gitlab.com/user/repo"));
}
fn test_build_rules_no_rules_default_deny() {
let args = Args {
rules: vec![],
config: None,
request_log: None,
interactive: false,
weak: false,
verbose: 0,
timeout: None,
no_jail_cleanup: false,
cleanup: false,
server: false,
command: vec![],
};

#[test]
fn test_rule_engine() {
let rules = vec![
Rule::new(Action::Allow, r"github\.com").unwrap(),
Rule::new(Action::Deny, r"telemetry").unwrap(),
Rule::new(Action::Deny, r".*").unwrap(),
];
let rules = build_rules(&args).unwrap();
assert!(rules.is_empty());

// Rule engine should deny requests when no rules are specified
let engine = RuleEngine::new(rules, None);

// Test allow rule
assert!(matches!(
engine.evaluate(Method::GET, "https://github.com/api"),
Action::Allow
));

// Test deny rule
assert!(matches!(
engine.evaluate(Method::POST, "https://telemetry.example.com"),
Action::Deny
));

// Test default deny
assert!(matches!(
engine.evaluate(Method::GET, "https://example.com"),
Action::Deny
Expand Down
10 changes: 10 additions & 0 deletions src/rules.rs
Original file line number Diff line number Diff line change
Expand Up @@ -226,4 +226,14 @@ mod tests {
let contents = std::fs::read_to_string(log_file.path()).unwrap();
assert!(contents.contains("- GET https://blocked.com"));
}

#[test]
fn test_default_deny_with_no_rules() {
let engine = RuleEngine::new(vec![], None);

assert!(matches!(
engine.evaluate(Method::GET, "https://example.com"),
Action::Deny
));
}
}
Loading