Harden Windows DLP enforcement and Electron runtime packaging
This commit is contained in:
1 parent
e7d778aeac
commit
8a253eda89
120 files changed
+3569
-135
No files matched your search
+70
-17
@@ -1,7 +1,10 @@
|
||||
const { app, BrowserWindow, WebContentsView, ipcMain, session, safeStorage, shell, dialog, powerMonitor, clipboard, Menu } = require('electron');
|
||||
const {assertSecureStartup}=require('./startup-security.cjs');
|
||||
const {assertPolicyAssignment}=require('./policy-assignment.cjs');
|
||||
assertSecureStartup(app.commandLine);
|
||||
try{assertSecureStartup(app.commandLine);}catch{
|
||||
console.error('Managed browser startup denied');
|
||||
process.exit(1);
|
||||
}
|
||||
// Force Chromium sandboxing globally before any renderer is created. The subframe
|
||||
// preload option below never grants a Node engine: native proof tests verify this.
|
||||
app.enableSandbox();
|
||||
@@ -20,7 +23,7 @@ function diagnosticSpan(name,attributes,operation){
|
||||
let diagnosticsConfiguration='';
|
||||
function configureDiagnostics(config){const value=config||{enabled:false},context=diagnosticContext(),key=JSON.stringify([value,context]);if(key===diagnosticsConfiguration)return;try{diagnostics.configure(value,context);diagnosticsConfiguration=key;}catch{diagnosticsConfiguration='';try{diagnostics.configure({enabled:false});}catch{}}}
|
||||
function diagnosticCorrelation(){try{const ids=diagnostics.correlation?.();return /^[a-f0-9]{32}$/.test(ids?.traceId||'')&&/[1-9a-f]/.test(ids.traceId)&&/^[a-f0-9]{16}$/.test(ids?.spanId||'')&&/[1-9a-f]/.test(ids.spanId)?{traceId:ids.traceId,spanId:ids.spanId}:{};}catch{return {};}}
|
||||
let win,view,browserSession,managementSession,core,device,policy,ready=false,syncing=false,aiPanel=false,vaultPanel=false,vault,vaultTimer,fileUploadGate,uploadLeaseFatal=false;
|
||||
let win,view,browserSession,managementSession,localRelay,core,device,policy,ready=false,syncing=false,aiPanel=false,vaultPanel=false,vault,vaultTimer,fileUploadGate,uploadLeaseFatal=false;
|
||||
let chromeHeight=104,leftSidebar=false,startPage=false,overviewPanel=false,toolsOpen=false,pageTitle='',pageColor='#edf1f6';
|
||||
const {createTabRegistry}=require('./tab-registry.cjs');
|
||||
let tabs,tabsRevision=0;
|
||||
@@ -39,7 +42,7 @@ const {startAgentControl}=require('./agent-control.cjs');
|
||||
const {createAgentAdapters}=require('./agent-adapters.cjs');
|
||||
const {createVault}=require('./vault.cjs');
|
||||
const {createDeveloperToolsGuard,developerAssetRequestAllowed}=require('./developer-tools.cjs');
|
||||
const {settingsState,sectionVisible,sectionAllowed,featureAllowed,requireFeature,requireSection,validateAppearance}=require('./managed-settings.cjs');
|
||||
const {settingsState,sectionAllowed,featureAllowed,requireFeature,requireSection,validateAppearance}=require('./managed-settings.cjs');
|
||||
const {bookmarkState,validateMutation:validateBookmarkMutation,libraryVisible}=require('./bookmarks.cjs');
|
||||
let bookmarkBusy=false,personalBookmarks={revision:0,items:[]},bookmarkSnapshotJSON='',libraryOrganizer;
|
||||
function effectiveBookmarkPolicy(){return policy?{...policy,browser:{...policy.browser,bookmarks:{...policy.browser.bookmarks,personal:personalBookmarks}}}:null;}
|
||||
@@ -180,7 +183,7 @@ async function pageTheme(tab=activeTab()){
|
||||
}catch{}
|
||||
}
|
||||
function lockVault(){const unlocked=vault&&!vault.status().locked;credentialBroker?.revoke();vault?.lock();if(unlocked)audit('vault.locked');emit();}
|
||||
function quarantine(reason){ready=false;fileUploadGate?.cancelAll();if(policy&&Date.parse(policy.expiresAt)<=Date.now()){configureDiagnostics({enabled:false});const expiredService=agentService;agentService=null;agentServiceMode=undefined;expiredService?.revoke();void expiredService?.close().catch(()=>{});}for(const [controller,route] of managementRequests)if(route==='/api/device/ai')controller.abort();enforceEvidencePolicy();message=reason;vaultPanel=false;vault?.lock();tabs?.quarantine();emit();}
|
||||
function quarantine(reason){ready=false;localRelay?.invalidate();fileUploadGate?.cancelAll();if(policy&&Date.parse(policy.expiresAt)<=Date.now()){configureDiagnostics({enabled:false});const expiredService=agentService;agentService=null;agentServiceMode=undefined;expiredService?.revoke();void expiredService?.close().catch(()=>{});}for(const [controller,route] of managementRequests)if(route==='/api/device/ai')controller.abort();enforceEvidencePolicy();message=reason;vaultPanel=false;vault?.lock();tabs?.quarantine();emit();}
|
||||
function vaultSettings(){return {enabled:ready&&!!policy?.passwordManager?.enabled,allowSaving:!!policy?.passwordManager?.allowSaving,allowAutofill:!!policy?.passwordManager?.allowAutofill,allowControlledSubmission:!!policy?.passwordManager?.controlledSubmission?.enabled,idleLockSeconds:policy?.passwordManager?.idleLockSeconds||300};}
|
||||
function vaultStatus(){return {...(vault?.status()||{exists:fs.existsSync(path.join(app.getPath('userData'),'password-vault.json')),locked:true}),...vaultSettings()};}
|
||||
function requireVault(){
|
||||
@@ -240,8 +243,9 @@ async function applyPolicyImpl(envelope){
|
||||
configureDiagnostics({enabled:false});quarantine('Applying company policy…');
|
||||
const proxy=next.proxy;
|
||||
const settings={mode:proxy.mode,...(proxy.mode==='fixed_servers'?{proxyRules:proxy.rules,proxyBypassRules:proxy.bypass}:{}),...(proxy.mode==='pac_script'?{pacScript:proxy.pacUrl}:{})};
|
||||
await browserSession.closeAllConnections();await browserSession.setProxy(settings);
|
||||
localRelay.invalidate();await browserSession.closeAllConnections();
|
||||
await managementSession.closeAllConnections();await managementSession.setProxy(settings);
|
||||
await browserSession.setProxy({mode:'fixed_servers',proxyRules:localRelay.proxyRules,proxyBypassRules:localRelay.proxyBypassRules});
|
||||
app.configureHostResolver(resolverOptions(next.enterprise?.dns));
|
||||
await browserSession.clearHostResolverCache();recentRequests.clear();
|
||||
await managementSession.clearHostResolverCache();
|
||||
@@ -375,6 +379,7 @@ function requireLibraryAI(){
|
||||
if(!ready||!policy?.ai.enabled||!policy.library?.aiSuggestionsEnabled||Date.parse(policy.expiresAt)<=Date.now())throw Error('AI library organization is disabled by company policy');
|
||||
if(!policy.browser.bookmarks.allowUserEdit)throw Error('Bookmark organization is controlled by your administrator');
|
||||
}
|
||||
// eslint-disable-next-line no-control-regex -- Strip control characters from security-sensitive metadata.
|
||||
function nativeLibraryRows(){return tabs.all().filter(tab=>!tab.startPage&&/^https?:\/\//.test(tab.view.webContents.getURL())).map(tab=>({id:tab.id,title:String(tab.title).replace(/[\u0000-\u001f\u007f]/g,' ').slice(0,120),url:tab.view.webContents.getURL()}));}
|
||||
function nativeLibraryEpoch(){return JSON.stringify(tabs.all().map(tab=>[tab.id,tab.navigationId,tab.title,tab.view.webContents.getURL(),tab.group]));}
|
||||
function assertAgentLibraryDestinations(snapshot){if(agentActorStorage.getStore()?.principalId&&[...(snapshot.tabs||[]),...(snapshot.bookmarks||[])].some(row=>!actorDestinationAllowed(row.url)))throw Error('Agent destination denied');}
|
||||
@@ -462,9 +467,46 @@ async function dnsGateImpl(url,currentPolicy){
|
||||
const addresses=resolved.endpoints?.map(v=>v.address)||[];
|
||||
if(!addresses.length||addresses.some(ip=>securityCore.dnsAddressBlocked(ip,currentPolicy)))throw Error('dns-ip-blocklist');
|
||||
}
|
||||
function inspectOutgoingText(bytes,currentPolicy,isCurrent){
|
||||
return require('./inspect-text-representations.cjs').inspectTextRepresentations({bytes,policy:currentPolicy,isCurrent,
|
||||
inspectLocal:localInspector?(data,p)=>localInspector.inspect(data,p):undefined,
|
||||
inspectEnterprise:(data,p)=>scanEnterprise(data,p,'upload'),
|
||||
traceOperation:(operation,facts,fn)=>diagnosticSpan(operation==='text.local'?'browser.inspect.local':'browser.inspect.enterprise',{operation,bytes:facts.bytes,count:facts.count},fn)});
|
||||
}
|
||||
function relayAuthorityCurrent(authority){return ready&&policy===authority.policy&&device?.id===authority.deviceId&&Date.parse(policy.expiresAt)>Date.now();}
|
||||
async function initializeLocalRelay(){
|
||||
localRelay=await require('./local-dlp-relay.cjs').startLocalDlpRelay({
|
||||
authorize:url=>{const authority={policy,deviceId:device?.id};if(!relayAuthorityCurrent(authority)||!core.destinationAllowed(url,policy)||core.inspect(Buffer.from(url),policy).decision!=='allow')throw Error('relay-target-refused');return authority;},
|
||||
isCurrent:relayAuthorityCurrent,
|
||||
resolveUpstream:url=>managementSession.resolveProxy(url),
|
||||
resolveAddress:async(host,authority)=>{const resolved=await managementSession.resolveHost(host,{cacheUsage:'allowed'}),addresses=resolved.endpoints?.map(endpoint=>endpoint.address)||[];if(!relayAuthorityCurrent(authority)||!addresses.length||addresses.some(address=>securityCore.dnsAddressBlocked(address,authority.policy)))throw Error('relay-address-refused');return addresses[0];},
|
||||
credentialsForProxy:async selected=>require('./proxy-credentials.cjs').deploymentProxyCredentials(selected)
|
||||
});
|
||||
// Every managed URL resolves to a non-DIRECT native proxy, including loopback.
|
||||
// Enterprise DIRECT/PAC/system choices apply only behind this private relay.
|
||||
await browserSession.setProxy({mode:'fixed_servers',proxyRules:localRelay.proxyRules,proxyBypassRules:localRelay.proxyBypassRules});
|
||||
}
|
||||
function responsePolicyHeaders(input){
|
||||
if(!input||typeof input!=='object'||Array.isArray(input))throw Error('Response policy headers unavailable');
|
||||
const headers={...input},keys=Object.keys(headers).filter(key=>key.toLowerCase()==='connection-allowlist');
|
||||
const values=keys.flatMap(key=>Array.isArray(headers[key])?headers[key]:[headers[key]]);
|
||||
if(values.some(value=>typeof value!=='string'))throw Error('Response policy headers unavailable');
|
||||
let existing=values.join(',');if(existing.length>65536)throw Error('Response policy headers unavailable');
|
||||
for(const key of keys)delete headers[key];
|
||||
if(existing){let quoted=false,escaped=false,depth=0,split=existing.length;
|
||||
for(let index=0;index<existing.length;index++){const ch=existing[index];if(escaped){escaped=false;continue;}if(quoted&&ch==='\\'){escaped=true;continue;}if(ch==='"'){quoted=!quoted;continue;}if(quoted)continue;if(ch==='(')depth++;if(ch===')'&&--depth<0)throw Error('Response policy headers unavailable');if(ch===','&&depth===0){split=index;break;}}
|
||||
if(quoted||depth!==0)throw Error('Response policy headers unavailable');
|
||||
existing=existing.slice(0,split).trim()+'; webrtc=block'+existing.slice(split);
|
||||
}else existing='("http://*:*/*" "https://*:*/*"); redirects=allow; webrtc=block';
|
||||
headers['Connection-Allowlist']=[existing];return headers;
|
||||
}
|
||||
function hooks(){
|
||||
browserSession.setPermissionRequestHandler((_wc,_permission,callback)=>callback(false));
|
||||
browserSession.setPermissionCheckHandler(()=>false);
|
||||
// Native Chromium policy blocks uninspected WebRTC; HTTP(S) retains normal DLP.
|
||||
browserSession.webRequest.onHeadersReceived({urls:['http://*/*','https://*/*']},(details,callback)=>{
|
||||
try{callback({cancel:false,responseHeaders:responsePolicyHeaders(details.responseHeaders)});}catch{callback({cancel:true});void audit('dlp.block','response-policy-uninspectable');}
|
||||
});
|
||||
const leaseRoot=app.isPackaged?process.resourcesPath:path.join(__dirname,'../..');
|
||||
fileUploadGate=require('./file-upload-gate.cjs').createFileUploadGate({
|
||||
maxBytes:context=>context.policy.dlp.maxBytes,
|
||||
@@ -527,12 +569,18 @@ function hooks(){
|
||||
if(!core.destinationAllowed(details.url,policy)||(source?.security.agentAuthority&&!actorDestinationAllowed(details.url,source.security.agentAuthority))){audit('navigation.block',null,new URL(details.url).hostname);finish(true);return;}
|
||||
if(credentialBroker&&source){try{credentialBroker.guardIssued(details,credentialContext(source));credentialBroker.validateRedirect(details,credentialContext(source));}catch{await audit('vault.submission-denied');message='Login submission blocked by company policy. Contact your administrator.';emit();finish(true);return;}}
|
||||
recentRequests.set(`${details.webContentsId}:${details.url}`,{method:details.method,time:Date.now()});if(recentRequests.size>2000)for(const [url,value]of recentRequests)if(value.time<Date.now()-120000||recentRequests.size>2000)recentRequests.delete(url);
|
||||
try{await dnsGate(details.url,initialPolicy);}catch(error){audit('dns.block',error.message,new URL(details.url).hostname);finish(true);return;}
|
||||
if(called||!ready||policy!==initialPolicy||!sourceCurrent()){if(!called&&ready&&policy===initialPolicy&&!sourceCurrent())await audit('navigation.block','request-source-context-changed',new URL(details.url).hostname);finish(true);return;}
|
||||
if(details.resourceType==='webSocket'&&!policy.browser.allowWebSockets){audit('dlp.block','websocket-disabled',new URL(details.url).hostname);finish(true);return;}
|
||||
const address=new URL(details.url),query=address.pathname+address.search;
|
||||
const queryVerdict=core.inspect(Buffer.from(query),policy);
|
||||
if(queryVerdict.decision==='block'){audit('dlp.block',queryVerdict.ruleId,new URL(details.url).hostname);finish(true);return;}
|
||||
if(details.resourceType==='webSocket'){audit('dlp.block','websocket-disabled',new URL(details.url).hostname);finish(true);return;}
|
||||
const address=new URL(details.url),urlBytes=Buffer.from(address.href);
|
||||
try{
|
||||
const urlVerdict=core.inspect(urlBytes,initialPolicy);
|
||||
if(urlVerdict.decision!=='allow'){await audit('dlp.block',urlVerdict.ruleId,address.hostname);finish(true);return;}
|
||||
const inspected=await inspectOutgoingText(urlBytes,initialPolicy,()=>!called&&ready&&policy===initialPolicy&&Date.parse(initialPolicy.expiresAt)>Date.now()&&sourceCurrent());
|
||||
if(inspected.decision!=='allow'){await audit('dlp.block',inspected.ruleId||'inspection-incomplete',address.hostname);finish(true);return;}
|
||||
}finally{urlBytes.fill(0);}
|
||||
if(called||!ready||policy!==initialPolicy||!sourceCurrent()){finish(true);return;}
|
||||
try{await dnsGate(details.url,initialPolicy);}catch(error){audit('dns.block',error.message,address.hostname);finish(true);return;}
|
||||
if(called||!ready||policy!==initialPolicy||!sourceCurrent()){finish(true);return;}
|
||||
const uploads=details.uploadData||[];
|
||||
if(policy.security.visualPhishing.enabled&&(uploads.length||(address.search&&details.resourceType==='xhr')||details.resourceType==='webSocket')){
|
||||
const checked=source?await checkVisual(source):{decision:'unknown'};if(checked.decision!=='allow'||!ready||policy!==initialPolicy||!sourceCurrent()){finish(true);return;}
|
||||
@@ -565,7 +613,7 @@ function hooks(){
|
||||
const needsLocal=verdict.ruleId==='uninspectable'||policy.dlp.semantic.enabled||policy.security.yara.enabled;
|
||||
if(needsLocal&&!localInspector){claim?.residual.fill(0);audit('dlp.block','inspector-unavailable',new URL(details.url).hostname);finish(true);return;}
|
||||
const snapshot=claim?Buffer.from(claim.residual):Buffer.concat(uploads.map(p=>Buffer.from(p.bytes))),currentPolicy=policy;claim?.residual.fill(0);
|
||||
try{let result=needsLocal?await diagnosticSpan('browser.inspect.local',{bytes:snapshot.length},()=>localInspector.inspect(snapshot,currentPolicy)):{decision:'allow'};if(result.decision==='allow')result=await diagnosticSpan('browser.inspect.enterprise',{bytes:snapshot.length},()=>scanEnterprise(snapshot,currentPolicy,'upload'));
|
||||
try{let result;if(claim||transfer.detectedType==='text'){result=await inspectOutgoingText(snapshot,currentPolicy,()=>!called&&ready&&policy===currentPolicy&&Date.parse(currentPolicy.expiresAt)>Date.now()&&sourceCurrent());}else{result=needsLocal?await diagnosticSpan('browser.inspect.local',{bytes:snapshot.length},()=>localInspector.inspect(snapshot,currentPolicy)):{decision:'allow'};if(result.decision==='allow')result=await diagnosticSpan('browser.inspect.enterprise',{bytes:snapshot.length},()=>scanEnterprise(snapshot,currentPolicy,'upload'));}
|
||||
const cancel=called||!ready||policy.version!==currentPolicy.version||!sourceCurrent()||result.decision!=='allow';
|
||||
if(cancel){audit('dlp.block',result.ruleId||'policy-changed',new URL(details.url).hostname);finish(true);}else await releaseUpload();
|
||||
}catch{audit('dlp.block','inspection-incomplete',new URL(details.url).hostname);finish(true);}finally{snapshot.fill(0);}return;
|
||||
@@ -748,6 +796,7 @@ app.whenReady().then(async()=>{
|
||||
core=await import('../../packages/core/policy.mjs');
|
||||
clipboardCore=await import('../../packages/core/clipboard.mjs');
|
||||
ipcMain.on('managed:clipboard-sandbox-failure',event=>{if(tabs?.fromContents(event.sender.id)||event.sender===win?.webContents){clipboardFatal=true;quarantine('Managed renderer security failed. Restart this browser.');}});
|
||||
ipcMain.on('managed:transport-guard-failure',event=>{if(tabs?.fromContents(event.sender.id)){clipboardFatal=true;quarantine('Managed renderer security failed. Restart this browser.');void diagnosticSpan('browser.security',{decision:'block',reason:'transport-guard-unavailable'},()=>audit('navigation.block','transport-guard-unavailable')).catch(()=>{});}});
|
||||
powerMonitor.on('lock-screen',lockVault);powerMonitor.on('suspend',lockVault);
|
||||
securityCore=await import('../../packages/core/security.mjs');
|
||||
localInspector=startInspector(app.isPackaged?process.resourcesPath:path.join(__dirname,'../..'),app.isPackaged,{onStages:stages=>diagnostics.importStages(stages)});
|
||||
@@ -755,6 +804,8 @@ app.whenReady().then(async()=>{
|
||||
// Management has its own cookie jar and no website request gate. It uses the
|
||||
// same company proxy and Chromium DNS configuration as managed website traffic.
|
||||
managementSession=session.fromPartition('persist:safetynet-management',{cache:false});
|
||||
await initializeLocalRelay();
|
||||
browserSession.registerPreloadScript({type:'frame',id:'managed-transport-deny',filePath:path.join(__dirname,'transport-deny.cjs')});
|
||||
browserSession.registerPreloadScript({type:'frame',id:'managed-clipboard-deny',filePath:path.join(__dirname,'clipboard-deny.cjs')});
|
||||
session.defaultSession.registerPreloadScript({type:'frame',id:'shell-clipboard-deny',filePath:path.join(__dirname,'clipboard-deny.cjs')});
|
||||
Menu.setApplicationMenu(null);
|
||||
@@ -767,7 +818,7 @@ app.whenReady().then(async()=>{
|
||||
stage:async args=>{const tab=tabs.fromContents(args.webContents.id);if(tab&&args.operation==='paste'&&policy?.security.visualPhishing.enabled&&(await checkVisual(tab)).decision!=='allow')throw Error('Login protection holds paste');return executeClipboard('stage',args);},
|
||||
validate:args=>executeClipboard('validate',args),commit:args=>executeClipboard('commit',args),
|
||||
discard:args=>executeClipboard('discard',{...args,url:'',operation:'copy',limit:0}),
|
||||
readClipboard:async limit=>{const items=await clipboard.read();if(items.length!==1)throw Error('Clipboard format unsupported');const item=items[0];if(!item.types.includes('text/plain')||item.types.some(type=>type!=='text/plain'&&!/^electron application\/osclipboard;format="(?:CF_TEXT|CF_UNICODETEXT|CF_LOCALE)"$/.test(type)))throw Error('Rich clipboard denied');const blob=await item.getType('text/plain');if(blob.size>limit)throw Error('Clipboard size limit');return blob.text();},
|
||||
readClipboard:async limit=>{const items=await clipboard.read();if(items.length!==1)throw Error('Clipboard format unsupported');const item=items[0];if(!item.types.includes('text/plain')||item.types.some(type=>type!=='text/plain'&&type!=='text/html'&&!/^electron application\/osclipboard;format="(?:CF_TEXT|CF_UNICODETEXT|CF_LOCALE|HTML Format)"$/.test(type)))throw Error('Rich clipboard denied');const blob=await item.getType('text/plain');if(blob.size>limit)throw Error('Clipboard size limit');return blob.text();},
|
||||
writeClipboard:text=>clipboard.writeText(text),
|
||||
inspect:async(bytes,currentPolicy,operation)=>{let verdict=clipboardCore.inspectClipboard(bytes,currentPolicy,operation);if(verdict.decision!=='allow')return verdict;if(currentPolicy.dlp.semantic.enabled||currentPolicy.security.yara.enabled){if(!localInspector)return {decision:'block',ruleId:'clipboard-inspector-unavailable'};verdict=await diagnosticSpan('browser.inspect.local',{bytes:bytes.length},()=>localInspector.inspect(bytes,currentPolicy));}if(verdict.decision!=='allow')return verdict;const external=await diagnosticSpan('browser.inspect.enterprise',{bytes:bytes.length},()=>scanEnterprise(bytes,currentPolicy,'clipboard'));return external.decision==='allow'?verdict:external;},
|
||||
onCompleted:async metadata=>{
|
||||
@@ -921,13 +972,15 @@ app.whenReady().then(async()=>{
|
||||
let wasLocked=true;vaultTimer=setInterval(()=>{const locked=vault?.status().locked??true;if(locked!==wasLocked){if(locked)audit('vault.locked');wasLocked=locked;emit();}},1000);
|
||||
}).catch(error=>{console.error(error.stack);app.quit();});
|
||||
app.on('window-all-closed',()=>app.quit());
|
||||
app.on('before-quit',()=>{fileUploadGate?.dispose();tabs?.close();});
|
||||
app.on('before-quit',()=>{void localRelay?.close().catch(()=>{});fileUploadGate?.dispose();tabs?.close();});
|
||||
app.on('will-quit',()=>{Promise.resolve(diagnostics.shutdown()).catch(()=>{});vault?.lock();clearInterval(vaultTimer);clearInterval(networkTimer);localInspector?.stop();agentService?.close();});
|
||||
app.on('certificate-error',(event,_wc,_url,_error,_certificate,callback)=>{event.preventDefault();callback(false);});
|
||||
app.on('login',(event,_wc,_details,authInfo,callback)=>{
|
||||
app.on('login',(event,wc,_details,authInfo,callback)=>{
|
||||
event.preventDefault();
|
||||
if(localRelay&&authInfo.isProxy&&authInfo.host==='127.0.0.1'&&authInfo.port===Number(localRelay.proxyRules.split(':').at(-1))){
|
||||
if(ready&&policy&&Date.parse(policy.expiresAt)>Date.now()&&(!wc||wc.session===browserSession)){const credentials=localRelay.credentials();callback(credentials.username,credentials.password);}else callback();return;
|
||||
}
|
||||
// Proxy credentials are supplied by deployment environment, never a page or policy bundle.
|
||||
if(authInfo.isProxy&&process.env.SAFETYNET_PROXY_USER&&process.env.SAFETYNET_PROXY_PASSWORD)callback(process.env.SAFETYNET_PROXY_USER,process.env.SAFETYNET_PROXY_PASSWORD);
|
||||
else callback();
|
||||
const credentials=authInfo.isProxy?require('./proxy-credentials.cjs').deploymentProxyCredentials(authInfo):null;
|
||||
if(credentials)callback(credentials.username,credentials.password);else callback();
|
||||
});
|
||||
|
||||
Reference in new issue
Block a user