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
35 changes: 10 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ httpjail -r "deny: telemetry\..*" -r "allow: .*" -- ./my-app
httpjail -r "allow-get: api\.github\.com" -r "deny: .*" -- git pull

# Use config file for complex rules
httpjail --config rules.yaml -- python script.py
httpjail --config rules.txt -- python script.py
```

## Architecture Overview
Expand Down Expand Up @@ -152,41 +152,26 @@ httpjail \

### Configuration File

Create a `rules.yaml`:
Create a `rules.txt` (one rule per line, `#` comments and blank lines are ignored):

```yaml
# rules.yaml
rules:
- action: allow
pattern: "github\.com"
methods: ["GET", "POST"]

- action: allow
pattern: "api\..*\.com"
methods: ["GET"]

- action: deny
pattern: "telemetry"

- action: deny
pattern: ".*"

logging:
level: info
file: /var/log/httpjail.log
```text
# rules.txt
allow-get: github\.com
deny: telemetry
allow: .*
```

Use the config:

```bash
httpjail --config rules.yaml -- ./my-application
httpjail --config rules.txt -- ./my-application
```

### Advanced Options

```bash
# Dry run - log what would be blocked without blocking
httpjail --dry-run --config rules.yaml -- ./app
httpjail --dry-run --config rules.txt -- ./app

# Verbose logging
httpjail -vvv --allow ".*" -- curl https://example.com
Expand Down Expand Up @@ -257,7 +242,7 @@ RULE FORMAT:

EXAMPLES:
httpjail -r "allow: github\.com" -r "deny: .*" -- git clone https://github.com/user/repo
httpjail --config rules.yaml -- npm install
httpjail --config rules.txt -- npm install
httpjail --dry-run -r "deny: telemetry" -r "allow: .*" -- ./application
httpjail --weak -r "allow: .*" -- npm test # Use environment variables only
```
Expand Down
58 changes: 56 additions & 2 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use anyhow::Result;
use anyhow::{Context, Result};
use clap::Parser;
use httpjail::jail::{JailConfig, create_jail};
use httpjail::proxy::ProxyServer;
Expand Down Expand Up @@ -175,7 +175,20 @@ fn parse_rule(rule_str: &str) -> Result<Rule> {
fn build_rules(args: &Args) -> Result<Vec<Rule>> {
let mut rules = Vec::new();

// Parse rules in the exact order specified
// Load rules from config file if provided
if let Some(config_path) = &args.config {
let contents = std::fs::read_to_string(config_path)
.with_context(|| format!("Failed to read config file: {}", config_path))?;
for line in contents.lines() {
let line = line.trim();
if line.is_empty() || line.starts_with('#') {
continue;
}
rules.push(parse_rule(line)?);
}
}

// Parse command line rules in the exact order specified
for rule_str in &args.rules {
rules.push(parse_rule(rule_str)?);
}
Expand Down Expand Up @@ -501,4 +514,45 @@ mod tests {
Action::Allow
));
}

#[test]
fn test_build_rules_from_config_file() {
use std::io::Write;
use tempfile::NamedTempFile;

let mut file = NamedTempFile::new().unwrap();
writeln!(
file,
"allow-get: google\\.com\n# comment\ndeny: yahoo.com\n\nallow: .*"
)
.unwrap();

let args = Args {
rules: vec![],
config: Some(file.path().to_str().unwrap().to_string()),
dry_run: false,
log_only: false,
no_tls_intercept: false,
interactive: false,
weak: false,
verbose: 0,
timeout: None,
no_jail_cleanup: false,
cleanup: false,
command: vec![],
};

let rules = build_rules(&args).unwrap();
assert_eq!(rules.len(), 3);

// First rule should be allow for GET method only
assert!(matches!(rules[0].action, Action::Allow));
assert!(rules[0].methods.as_ref().unwrap().contains(&Method::GET));

// Second rule should be deny
assert!(matches!(rules[1].action, Action::Deny));

// Third rule allow all
assert!(matches!(rules[2].action, Action::Allow));
}
}