fn env_flag(key: &str) -> Option<bool> {
match std::env::var(key).ok().as_deref().map(str::trim) {
Some("1") | Some("on") | Some("true") | Some("yes") => Some(true),
Some("0") | Some("off") | Some("false") | Some("no") => Some(false),
_ => None,
}
}
pub(crate) fn detect_container() -> bool {
if std::path::Path::new("/.dockerenv").exists()
|| std::path::Path::new("/run/.containerenv").exists()
{
return true;
}
let hit = |p: &str| {
std::fs::read_to_string(p)
.map(|s| {
let s = s.to_ascii_lowercase();
s.contains("docker")
|| s.contains("kubepods")
|| s.contains("containerd")
|| s.contains("/lxc/")
|| s.contains("libpod")
|| s.contains("podman")
})
.unwrap_or(false)
};
hit("/proc/1/cgroup") || hit("/proc/self/cgroup")
}
pub(crate) fn has_dri_render_node() -> bool {
if !cfg!(target_os = "linux") {
return true;
}
match std::fs::read_dir("/dev/dri") {
Ok(entries) => entries.flatten().any(|e| {
e.file_name()
.to_str()
.map(|n| n.starts_with("renderD"))
.unwrap_or(false)
}),
Err(_) => false,
}
}
pub(crate) fn should_spoof(stealth: bool, forced: Option<bool>) -> bool {
if !cfg!(target_os = "linux") {
return false;
}
if let Some(f) = forced {
return f;
}
if let Some(f) = env_flag("DRISSION_CONTAINER_SPOOF") {
return f;
}
stealth && (detect_container() || !has_dri_render_node())
}
pub(crate) fn spoof_vendor(explicit: Option<&str>) -> String {
explicit
.map(str::to_string)
.or_else(|| std::env::var("DRISSION_WEBGL_VENDOR").ok())
.filter(|s| !s.trim().is_empty())
.unwrap_or_else(|| "Google Inc. (Intel)".to_string())
}
pub(crate) fn spoof_renderer(explicit: Option<&str>) -> String {
explicit
.map(str::to_string)
.or_else(|| std::env::var("DRISSION_WEBGL_RENDERER").ok())
.filter(|s| !s.trim().is_empty())
.unwrap_or_else(|| {
"ANGLE (Intel, Mesa Intel(R) UHD Graphics 620 (KBL GT2), OpenGL 4.6 (Core Profile) Mesa 22.3.6)"
.to_string()
})
}
pub(crate) fn spoof_js(vendor: &str, renderer: &str) -> String {
SPOOF_JS_TEMPLATE
.replace("__VENDOR__", &json_str(vendor))
.replace("__RENDERER__", &json_str(renderer))
}
fn json_str(s: &str) -> String {
serde_json::to_string(s).unwrap_or_else(|_| "\"\"".into())
}
const SPOOF_JS_TEMPLATE: &str = r#"(function(){
try{
var V=__VENDOR__, R=__RENDERER__;
var wrap=function(proto){
if(!proto) return;
var gp=proto.getParameter;
if(!gp || gp.__dfc) return;
var np=new Proxy(gp,{
apply:function(t,self,args){
var p=args[0];
if(p===37445) return V; // UNMASKED_VENDOR_WEBGL
if(p===37446) return R; // UNMASKED_RENDERER_WEBGL
return Reflect.apply(t,self,args);
},
get:function(t,k,r){ if(k==='__dfc') return true; return Reflect.get(t,k,r); }
});
try{ proto.getParameter=np; }catch(e){}
};
if(window.WebGLRenderingContext) wrap(WebGLRenderingContext.prototype);
if(window.WebGL2RenderingContext) wrap(WebGL2RenderingContext.prototype);
}catch(e){}
})();"#;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn env_flag_parses_common_truthy_falsy() {
unsafe { std::env::set_var("DRISSION_TEST_FLAG_X", "1") };
assert_eq!(env_flag("DRISSION_TEST_FLAG_X"), Some(true));
unsafe { std::env::set_var("DRISSION_TEST_FLAG_X", "off") };
assert_eq!(env_flag("DRISSION_TEST_FLAG_X"), Some(false));
unsafe { std::env::set_var("DRISSION_TEST_FLAG_X", "maybe") };
assert_eq!(env_flag("DRISSION_TEST_FLAG_X"), None);
unsafe { std::env::remove_var("DRISSION_TEST_FLAG_X") };
assert_eq!(env_flag("DRISSION_TEST_FLAG_X"), None);
}
#[test]
fn non_linux_never_spoofs() {
if !cfg!(target_os = "linux") {
assert!(!should_spoof(true, Some(true)));
assert!(!should_spoof(true, None));
}
}
#[test]
fn linux_forced_option_wins() {
if cfg!(target_os = "linux") {
assert!(
should_spoof(false, Some(true)),
"显式 true 强制开,忽略 stealth"
);
assert!(!should_spoof(true, Some(false)), "显式 false 强制关");
}
}
#[test]
fn vendor_renderer_defaults_and_explicit() {
assert!(spoof_vendor(None).contains("Intel"));
let r = spoof_renderer(None);
assert!(r.contains("ANGLE") && r.contains("Mesa") && r.contains("Intel"));
assert!(!r.contains("SwiftShader") && !r.contains("llvmpipe"));
assert_eq!(
spoof_vendor(Some("Google Inc. (NVIDIA)")),
"Google Inc. (NVIDIA)"
);
assert_eq!(
spoof_renderer(Some("ANGLE (NVIDIA, ...)")),
"ANGLE (NVIDIA, ...)"
);
}
#[test]
fn spoof_js_hooks_unmasked_params_with_proxy() {
let js = spoof_js(
"Google Inc. (Intel)",
"ANGLE (Intel, Mesa Intel(R) UHD Graphics 620 (KBL GT2), OpenGL 4.6)",
);
assert!(js.contains("37445") && js.contains("37446"));
assert!(js.contains("Mesa Intel(R) UHD Graphics 620"));
assert!(js.contains("new Proxy") && js.contains("Reflect.apply"));
assert!(js.contains("__dfc"));
assert!(js.starts_with("(function()") && js.trim_end().ends_with("})();"));
assert!(!js.contains("SwiftShader") && !js.contains("llvmpipe"));
}
}