Download src/hk_browser_capability.erl from Snapkitty/hyperkitty-chromium: direct link, hf CLI and curl.
- Browser
- Download file 4.66 kB
-
https://huggingface.co/Snapkitty/hyperkitty-chromium/resolve/main/src/hk_browser_capability.erl
- Command line
-
hf download hf://Snapkitty/hyperkitty-chromium/src/hk_browser_capability.erl
-
curl -L -o hk_browser_capability.erl https://huggingface.co/Snapkitty/hyperkitty-chromium/resolve/main/src/hk_browser_capability.erl
4.66 kB
| %%%------------------------------------------------------------------- | |
| %%% @doc HK-AGENT / HK-BROWSER boundary :: the typed browser | |
| %%% capability interface and its authorization + argument checks. | |
| %%% | |
| %%% This module is the *only* place that (a) enumerates which | |
| %%% browser operations exist, (b) decides whether a given agent's | |
| %%% granted capability list permits a specific call, and | |
| %%% (c) validates that call's arguments -- before anything reaches | |
| %%% `hk_browser_session'. Per the security model | |
| %%% ("Validate all tool arguments at the control-plane boundary" / | |
| %%% "Do not treat an agent prompt as a security boundary"), this | |
| %%% check happens in `hk_agent_fsm' (the control plane), not inside | |
| %%% whatever produced the agent's plan. An agent with no granted | |
| %%% capability for an operation cannot reach Chromium for it at all | |
| %%% -- there is no fallback "just try it" path. | |
| %%% @end | |
| %%%------------------------------------------------------------------- | |
| -module(hk_browser_capability). | |
| -export([operations/0, is_granted/2, validate_args/2]). | |
| -type operation() :: create_session | close_session | open_tab | close_tab | |
| | navigate | back | forward | reload | read_page | |
| | query_element | click | type | scroll | screenshot | |
| | extract_links. | |
| -export_type([operation/0]). | |
| -spec operations() -> [operation()]. | |
| operations() -> | |
| [create_session, close_session, open_tab, close_tab, | |
| navigate, back, forward, reload, read_page, | |
| query_element, click, type, scroll, screenshot, extract_links]. | |
| %% @doc `Capabilities' is the list granted to an agent (see | |
| %% `#agent.capabilities'). A capability entry is either a bare atom | |
| %% (unrestricted grant for that operation) or `{Op, Constraints}' | |
| %% for operations that support finer scoping -- currently `navigate' | |
| %% supports `#{allowed_domains => [binary()]}'. | |
| -spec is_granted([term()], operation() | {operation(), map()}) -> boolean(). | |
| is_granted(Capabilities, {navigate, #{url := Url}}) -> | |
| lists:any( | |
| fun (navigate) -> true; | |
| ({navigate, #{allowed_domains := Domains}}) -> host_allowed(Url, Domains); | |
| (_) -> false | |
| end, | |
| Capabilities); | |
| is_granted(Capabilities, Op) when is_atom(Op) -> | |
| lists:any( | |
| fun (G) when G =:= Op -> true; | |
| ({G, _}) when G =:= Op -> true; | |
| (_) -> false | |
| end, | |
| Capabilities). | |
| %% @doc Structural + semantic validation of an operation's arguments. | |
| %% Returns `{ok, NormalizedArgs}' or `{error, Reason}'. This is | |
| %% deliberately independent of authorization (`is_granted/2') -- | |
| %% malformed arguments are rejected the same way whether or not the | |
| %% operation would otherwise be permitted. | |
| -spec validate_args(operation(), map()) -> {ok, map()} | {error, term()}. | |
| validate_args(navigate, #{url := Url} = Args) when is_binary(Url) -> | |
| case valid_url(Url) of | |
| true -> {ok, Args}; | |
| false -> {error, {invalid_url, Url}} | |
| end; | |
| validate_args(navigate, _) -> | |
| {error, missing_url}; | |
| validate_args(open_tab, #{url := Url} = Args) when is_binary(Url) -> | |
| case valid_url(Url) of | |
| true -> {ok, Args}; | |
| false -> {error, {invalid_url, Url}} | |
| end; | |
| validate_args(Op, #{selector := Selector} = Args) | |
| when Op =:= click; Op =:= type; Op =:= query_element -> | |
| case is_binary(Selector) andalso byte_size(Selector) > 0 andalso byte_size(Selector) < 2048 of | |
| true when Op =:= type -> | |
| case Args of | |
| #{text := Text} when is_binary(Text), byte_size(Text) =< 10000 -> {ok, Args}; | |
| #{text := _} -> {error, text_too_long}; | |
| _ -> {error, missing_text} | |
| end; | |
| true -> {ok, Args}; | |
| false -> {error, invalid_selector} | |
| end; | |
| validate_args(scroll, #{delta_y := Dy} = Args) when is_integer(Dy), Dy >= -100000, Dy =< 100000 -> | |
| {ok, Args}; | |
| validate_args(Op, Args) when Op =:= close_session; Op =:= close_tab; Op =:= back; | |
| Op =:= forward; Op =:= reload; Op =:= read_page; | |
| Op =:= screenshot; Op =:= extract_links; Op =:= create_session -> | |
| {ok, Args}; | |
| validate_args(Op, _Args) -> | |
| {error, {invalid_arguments, Op}}. | |
| %%% internal | |
| valid_url(Url) -> | |
| case uri_string:parse(Url) of | |
| #{scheme := Scheme} when Scheme =:= <<"http">>; Scheme =:= <<"https">>; | |
| Scheme =:= <<"about">>; Scheme =:= <<"data">> -> | |
| true; | |
| _ -> | |
| false | |
| end. | |
| host_allowed(Url, Domains) -> | |
| case uri_string:parse(Url) of | |
| #{host := Host} -> lists:member(Host, Domains); | |
| _ -> false | |
| end. | |