From c990aef0e9ebced2e43419fc0de0c97580302b1e Mon Sep 17 00:00:00 2001 From: Edward Shen Date: Tue, 31 Dec 2019 19:12:17 -0500 Subject: [PATCH] execute from local file if possible --- .vscode/settings.json | 3 ++- bunbun.default.yaml | 11 ++++++++++ src/routes.rs | 51 ++++++++++++++++++++++++++++--------------- 3 files changed, 46 insertions(+), 19 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index d5156c6..548102b 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -6,5 +6,6 @@ "bunbunsearch", "itertools", "opensearchdescription" - ] + ], + "python.pythonPath": "/usr/bin/python3" } \ No newline at end of file diff --git a/bunbun.default.yaml b/bunbun.default.yaml index 3649e16..5eb7f5e 100644 --- a/bunbun.default.yaml +++ b/bunbun.default.yaml @@ -15,6 +15,17 @@ default_route: "g" # contain "{{query}}", which will be populated by the user's search query. This # input is percent-escaped. If multiple routes are defined, then the later # defined route is used. +# +# You may provide an (absolute, recommended) path to an executable file to out- +# source route resolution to a program. The program will receive one argument +# only, which is the entire string provided from the user after matching the +# route. It is up to the out-sourced program to parse the arguments and to +# interpret those arguments. These programs should print one line to standard +# out, which should be a fully resolved URL to lead the user to. +# +# These programs must be developed defensively, as they accept arbitrary user +# input. Improper handling of user input can easily lead to anywhere from simple +# flakey responses to remote code execution. groups: - name: "Meta commands" diff --git a/src/routes.rs b/src/routes.rs index 7158b18..4c2990b 100644 --- a/src/routes.rs +++ b/src/routes.rs @@ -6,11 +6,12 @@ use actix_web::web::{Data, Query}; use actix_web::{HttpRequest, HttpResponse, Responder}; use handlebars::Handlebars; use itertools::Itertools; -use log::debug; +use log::{debug, error}; use percent_encoding::{utf8_percent_encode, AsciiSet, CONTROLS}; use serde::{de::Visitor, Deserialize, Deserializer, Serialize, Serializer}; use std::collections::HashMap; use std::fmt; +use std::process::Command; use std::sync::{Arc, RwLock}; /// https://url.spec.whatwg.org/#fragment-percent-encode-set @@ -112,24 +113,34 @@ pub async fn hop( let data = data.read().unwrap(); match resolve_hop(&query.to, &data.routes, &data.default_route) { - (Some(path), args) => HttpResponse::Found() - .header( - header::LOCATION, - req - .app_data::() - .unwrap() - .render_template( - match path { - Route::Path(s) => s, // TODO: try resolve path - Route::External(s) => s, - }, - &template_args::query( - utf8_percent_encode(&args, FRAGMENT_ENCODE_SET).to_string(), - ), + (Some(path), args) => { + let resolved_template = match path { + Route::Path(path) => resolve_path(path, &args), + Route::External(path) => Ok(path.to_owned().into_bytes()), + }; + + match resolved_template { + Ok(path) => HttpResponse::Found() + .header( + header::LOCATION, + req + .app_data::() + .unwrap() + .render_template( + &std::str::from_utf8(&path).unwrap().trim(), + &template_args::query( + utf8_percent_encode(&args, FRAGMENT_ENCODE_SET).to_string(), + ), + ) + .unwrap(), ) - .unwrap(), - ) - .finish(), + .finish(), + Err(e) => { + error!("Failed to resolve template for path {}: {}", path, e); + HttpResponse::InternalServerError().body("Something went wrong :(") + } + } + } (None, _) => HttpResponse::NotFound().body("not found"), } } @@ -203,6 +214,10 @@ pub async fn index(data: StateData, req: HttpRequest) -> impl Responder { ) } +fn resolve_path(path: &str, args: &str) -> Result, crate::BunBunError> { + Ok(Command::new(path).arg(args).output()?.stdout) +} + #[get("/bunbunsearch.xml")] pub async fn opensearch(data: StateData, req: HttpRequest) -> impl Responder { let data = data.read().unwrap();