SimpleChatTC:SimpleProxy: once in a bluemoon transformed bearer

instead of using the shared bearer token as is, hash it with
current year and use the hash.

keep /aum path out of auth check.

in future bearer token could be transformed more often, as well as
with additional nounce/dynamic token from server got during initial
/aum handshake as also running counter and so ...

NOTE: All these circus not good enough, given that currently the
simpleproxy.py handshakes work over http. However these skeletons
put in place, for future, if needed.

TODO: There is a once in a bluemoon race when the year transitions
between client generating the request and server handling the req.
But other wise year transitions dont matter bcas client always
creates fresh token, and server checks for year change to genrate
fresh token if required.
This commit is contained in:
hanishkvc 2025-10-27 15:44:17 +05:30
parent 0552ff9098
commit 84403973cd
3 changed files with 52 additions and 14 deletions

View File

@ -32,6 +32,7 @@ gMe = {
'--port': 3128,
'--config': '/dev/null',
'--debug': False,
'bearer.transformed.year': "",
'server': None
}
@ -46,6 +47,22 @@ gConfigType = {
gConfigNeeded = [ '--allowed.domains', '--bearer.insecure' ]
def bearer_transform():
"""
Transform the raw bearer token to the network handshaked token,
if and when needed.
"""
global gMe
year = str(time.gmtime().tm_year)
if gMe['bearer.transformed.year'] == year:
return
import hashlib
s256 = hashlib.sha256(year.encode('utf-8'))
s256.update(gMe['--bearer.insecure'].encode('utf-8'))
gMe['--bearer.transformed'] = s256.hexdigest()
gMe['bearer.transformed.year'] = year
class ProxyHandler(http.server.BaseHTTPRequestHandler):
"""
Implements the logic for handling requests sent to this server.
@ -75,6 +92,7 @@ class ProxyHandler(http.server.BaseHTTPRequestHandler):
Simple Bearer authorization
ALERT: For multiple reasons, this is a very insecure implementation.
"""
bearer_transform()
authline = self.headers['Authorization']
if authline == None:
return { 'AllOk': False, 'Msg': "No auth line" }
@ -83,7 +101,7 @@ class ProxyHandler(http.server.BaseHTTPRequestHandler):
return { 'AllOk': False, 'Msg': "Invalid auth line" }
if authlineA[0] != 'Bearer':
return { 'AllOk': False, 'Msg': "Invalid auth type" }
if authlineA[1] != gMe['--bearer.insecure']:
if authlineA[1] != gMe['--bearer.transformed']:
return { 'AllOk': False, 'Msg': "Invalid auth" }
return { 'AllOk': True, 'Msg': "Auth Ok" }
@ -93,17 +111,21 @@ class ProxyHandler(http.server.BaseHTTPRequestHandler):
"""
print(f"\n\n\nDBUG:ProxyHandler:GET:{self.address_string()}:{self.path}")
print(f"DBUG:PH:Get:Headers:{self.headers}")
acGot = self.auth_check()
if not acGot['AllOk']:
self.send_error(400, f"WARN:{acGot['Msg']}")
return
pr = urllib.parse.urlparse(self.path)
print(f"DBUG:ProxyHandler:GET:{pr}")
match pr.path:
case '/urlraw':
handle_urlraw(self, pr)
acGot = self.auth_check()
if not acGot['AllOk']:
self.send_error(400, f"WARN:{acGot['Msg']}")
else:
handle_urlraw(self, pr)
case '/urltext':
handle_urltext(self, pr)
acGot = self.auth_check()
if not acGot['AllOk']:
self.send_error(400, f"WARN:{acGot['Msg']}")
else:
handle_urltext(self, pr)
case '/aum':
handle_aum(self, pr)
case _:

View File

@ -89,7 +89,12 @@ remember to
content like head, scripts, styles, headers, footers, ... Be careful when accessing web through this and
use it only with known safe sites.
* it allows one to specify a white list of allowed.domains, look into local.tools/simpleproxy.json
* look into local.tools/simpleproxy.json for specifying
* the white list of allowed.domains
* the shared bearer token between server and client ui
### using the front end
@ -228,6 +233,10 @@ It is attached to the document object. Some of these can also be updated using t
* proxyUrl - specify the address for the running instance of bundled local.tools/simpleproxy.py
* proxyAuthInsecure - shared token between simpleproxy.py server and client ui, for accessing service provided by it.
Shared token is currently hashed with the current year and inturn handshaked over the network. In future if required one could also include a dynamic token provided by simpleproxy server during /aum handshake and running counter or so into hashed token. ALERT: However do remember that currently the handshake occurs over http and not https, so others can snoop the network and get token. Per client ui running counter and random dynamic token can help mitigate things to some extent, if required in future.
* searchUrl - specify the search engine's search url template along with the tag SEARCHWORDS in place where the search words should be substituted at runtime.
* toolCallResponseTimeoutMS - specifies the time (in msecs) for which the logic should wait for a tool call to respond
@ -419,7 +428,8 @@ The bundled simple proxy
* tools/server/public_simplechat/local.tools/simpleproxy.py
* it provides for a basic white list of allowed domains to access, to be specified by the end user.
This should help limit web access to a safe set of sites determined by the end user.
This should help limit web access to a safe set of sites determined by the end user. There is also
a provision for shared bearer token to be specified by the end user.
* it tries to mimic the client/browser making the request to it by propogating header entries like
user-agent, accept and accept-language from the got request to the generated request during proxying

View File

@ -27,6 +27,13 @@ function get_gme() {
}
function bearer_transform() {
let data = `${new Date().getUTCFullYear()}${get_gme().tools.proxyAuthInsecure}`
return crypto.subtle.digest('sha-256', new TextEncoder().encode(data)).then(ab=>{
return Array.from(new Uint8Array(ab)).map(b=>b.toString(16).padStart(2,'0')).join('')
})
}
/**
* Helper http get logic wrt the bundled SimpleProxy server,
* which helps execute a given proxy dependent tool call.
@ -43,10 +50,11 @@ function get_gme() {
* @param {string} qkey
* @param {string} qvalue
*/
function proxyserver_get_1arg(toolcallid, toolname, obj, path, qkey, qvalue) {
async function proxyserver_get_1arg(toolcallid, toolname, obj, path, qkey, qvalue) {
if (gToolsWorker.onmessage != null) {
let newUrl = `${get_gme().tools.proxyUrl}/${path}?${qkey}=${qvalue}`
fetch(newUrl, { headers: { 'Authorization': `Bearer ${get_gme().tools.proxyAuthInsecure}` }}).then(resp => {
let btoken = await bearer_transform()
fetch(newUrl, { headers: { 'Authorization': `Bearer ${btoken}` }}).then(resp => {
if (!resp.ok) {
throw new Error(`${resp.status}:${resp.statusText}`);
}
@ -70,9 +78,7 @@ function proxyserver_get_1arg(toolcallid, toolname, obj, path, qkey, qvalue) {
* @param {Object<string, Object<string, any>>} tcs
*/
async function proxyserver_tc_setup(tag, tcPath, tcName, tcsData, tcs) {
await fetch(`${get_gme().tools.proxyUrl}/aum?url=${tcPath}.jambudweepe.akashaganga.multiverse.987654321123456789`, {
headers: { 'Authorization': `Bearer ${get_gme().tools.proxyAuthInsecure}` }
}).then(resp=>{
await fetch(`${get_gme().tools.proxyUrl}/aum?url=${tcPath}.jambudweepe.akashaganga.multiverse.987654321123456789`).then(resp=>{
if (resp.statusText != 'bharatavarshe') {
console.log(`WARN:ToolWeb:${tag}:Dont forget to run the bundled local.tools/simpleproxy.py to enable me`)
return