deepsitetest / index.html
Naresh1993's picture
Add 1 files
b9401aa verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Salesforce PII Analyzer</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
.loading-spinner {
width: 40px;
height: 40px;
border: 4px solid rgba(0, 0, 0, 0.1);
border-radius: 50%;
border-top-color: #3498db;
animation: spin 1s ease-in-out infinite;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
.pii-high {
background-color: #fee2e2;
border-left: 4px solid #ef4444;
}
.pii-medium {
background-color: #fef3c7;
border-left: 4px solid #f59e0b;
}
.pii-low {
background-color: #dbeafe;
border-left: 4px solid #3b82f6;
}
.slide-in {
animation: slideIn 0.3s ease-out forwards;
}
@keyframes slideIn {
from { transform: translateY(20px); opacity: 0; }
to { transform: translateY(0); opacity: 1; }
}
.progress-bar {
height: 6px;
background-color: #e5e7eb;
border-radius: 3px;
overflow: hidden;
}
.progress-fill {
height: 100%;
background-color: #10b981;
transition: width 0.3s ease;
}
</style>
</head>
<body class="bg-gray-50 min-h-screen">
<div class="container mx-auto px-4 py-8 max-w-6xl">
<header class="mb-8">
<div class="flex items-center justify-between">
<div>
<h1 class="text-3xl font-bold text-gray-800 flex items-center">
<i class="fas fa-shield-alt text-blue-500 mr-3"></i>
Salesforce PII Analyzer
</h1>
<p class="text-gray-600 mt-2">Identify potential Personally Identifiable Information in your Salesforce org</p>
</div>
<div id="loginButton" class="bg-blue-500 hover:bg-blue-600 text-white px-4 py-2 rounded-lg shadow-md transition-colors cursor-pointer flex items-center">
<i class="fab fa-salesforce mr-2"></i>
Login with Salesforce
</div>
<div id="userInfo" class="hidden items-center">
<img id="userPhoto" class="w-10 h-10 rounded-full mr-3" src="" alt="User photo">
<div>
<div id="userName" class="font-medium text-gray-800"></div>
<div id="orgName" class="text-sm text-gray-500"></div>
</div>
<button id="logoutButton" class="ml-4 text-gray-500 hover:text-gray-700">
<i class="fas fa-sign-out-alt"></i>
</button>
</div>
</div>
</header>
<div id="appContent" class="hidden">
<div class="bg-white rounded-xl shadow-md p-6 mb-6">
<h2 class="text-xl font-semibold text-gray-800 mb-4">Org Analysis</h2>
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 mb-6">
<div class="bg-gray-50 p-4 rounded-lg border border-gray-200">
<div class="text-gray-500 text-sm mb-1">Total Objects</div>
<div id="totalObjects" class="text-2xl font-bold text-gray-800">0</div>
</div>
<div class="bg-gray-50 p-4 rounded-lg border border-gray-200">
<div class="text-gray-500 text-sm mb-1">Potential PII Fields</div>
<div id="piiFields" class="text-2xl font-bold text-red-600">0</div>
</div>
<div class="bg-gray-50 p-4 rounded-lg border border-gray-200">
<div class="text-gray-500 text-sm mb-1">Analysis Progress</div>
<div class="progress-bar mt-2">
<div id="progressFill" class="progress-fill" style="width: 0%"></div>
</div>
<div id="progressText" class="text-sm text-gray-600 mt-1 text-right">0%</div>
</div>
</div>
<div class="flex items-center justify-between mb-4">
<h3 class="font-medium text-gray-700">Object Analysis Results</h3>
<div class="relative">
<select id="filterSelect" class="appearance-none bg-white border border-gray-300 rounded-md pl-3 pr-8 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500">
<option value="all">All Objects</option>
<option value="pii">Only PII Objects</option>
<option value="standard">Standard Objects</option>
<option value="custom">Custom Objects</option>
</select>
<div class="pointer-events-none absolute inset-y-0 right-0 flex items-center px-2 text-gray-700">
<i class="fas fa-chevron-down text-xs"></i>
</div>
</div>
</div>
<div id="resultsContainer" class="space-y-3">
<!-- Results will be populated here -->
</div>
</div>
<div id="piiGuidance" class="bg-white rounded-xl shadow-md p-6">
<h2 class="text-xl font-semibold text-gray-800 mb-4">PII Identification Guidance</h2>
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
<div class="p-4 rounded-lg pii-high">
<div class="flex items-center mb-2">
<div class="w-3 h-3 rounded-full bg-red-500 mr-2"></div>
<h3 class="font-medium">High Risk PII</h3>
</div>
<p class="text-sm text-gray-700">Fields that clearly contain personal data like SSN, credit card numbers, full names, etc.</p>
</div>
<div class="p-4 rounded-lg pii-medium">
<div class="flex items-center mb-2">
<div class="w-3 h-3 rounded-full bg-yellow-500 mr-2"></div>
<h3 class="font-medium">Potential PII</h3>
</div>
<p class="text-sm text-gray-700">Fields that might contain personal data based on naming patterns or field types.</p>
</div>
<div class="p-4 rounded-lg pii-low">
<div class="flex items-center mb-2">
<div class="w-3 h-3 rounded-full bg-blue-500 mr-2"></div>
<h3 class="font-medium">Low Risk</h3>
</div>
<p class="text-sm text-gray-700">Fields that are unlikely to contain personal data but should still be reviewed.</p>
</div>
</div>
</div>
</div>
<div id="loginState" class="flex flex-col items-center justify-center py-20">
<div class="bg-white rounded-xl shadow-md p-8 max-w-md w-full text-center">
<i class="fab fa-salesforce text-5xl text-blue-500 mb-4"></i>
<h2 class="text-2xl font-bold text-gray-800 mb-2">Salesforce PII Analyzer</h2>
<p class="text-gray-600 mb-6">Login with your Salesforce account to analyze your org for potential Personally Identifiable Information (PII) data.</p>
<div id="loginButtonCenter" class="bg-blue-500 hover:bg-blue-600 text-white px-6 py-3 rounded-lg shadow-md transition-colors cursor-pointer flex items-center justify-center mx-auto w-full max-w-xs">
<i class="fab fa-salesforce mr-2"></i>
Login with Salesforce
</div>
</div>
</div>
<div id="loadingState" class="hidden flex flex-col items-center justify-center py-20">
<div class="bg-white rounded-xl shadow-md p-8 max-w-md w-full text-center">
<div class="loading-spinner mx-auto mb-4"></div>
<h2 class="text-xl font-bold text-gray-800 mb-2" id="loadingText">Connecting to Salesforce...</h2>
<p class="text-gray-600" id="loadingSubtext">Please wait while we authenticate your session.</p>
</div>
</div>
</div>
<script>
// Configuration
const CLIENT_ID = '3MVG9pRzvMkjMb6lZlt3YjDQwe2tA1wWE4jHVJ.VZJj4i8T6gJQBWmwdVJZqgNrm4sY.sp0SJQCH2TfULXjG_';
const CALLBACK_URL = 'https://login.salesforce.com/services/oauth2/success';
const LOGIN_URL = 'https://login.salesforce.com';
const API_VERSION = '56.0';
// DOM elements
const loginButton = document.getElementById('loginButton');
const loginButtonCenter = document.getElementById('loginButtonCenter');
const logoutButton = document.getElementById('logoutButton');
const userInfo = document.getElementById('userInfo');
const userName = document.getElementById('userName');
const userPhoto = document.getElementById('userPhoto');
const orgName = document.getElementById('orgName');
const appContent = document.getElementById('appContent');
const loginState = document.getElementById('loginState');
const loadingState = document.getElementById('loadingState');
const loadingText = document.getElementById('loadingText');
const loadingSubtext = document.getElementById('loadingSubtext');
const resultsContainer = document.getElementById('resultsContainer');
const totalObjects = document.getElementById('totalObjects');
const piiFields = document.getElementById('piiFields');
const progressFill = document.getElementById('progressFill');
const progressText = document.getElementById('progressText');
const filterSelect = document.getElementById('filterSelect');
// State
let accessToken = null;
let instanceUrl = null;
let userId = null;
let orgId = null;
let objectsData = [];
let piiCount = 0;
// Initialize
document.addEventListener('DOMContentLoaded', () => {
// Check for OAuth response in URL
if (window.location.hash) {
handleOAuthResponse();
}
// Set up event listeners
loginButton.addEventListener('click', initiateOAuth);
loginButtonCenter.addEventListener('click', initiateOAuth);
logoutButton.addEventListener('click', logout);
filterSelect.addEventListener('change', filterResults);
});
// OAuth Functions
function initiateOAuth() {
const authUrl = `${LOGIN_URL}/services/oauth2/authorize?response_type=token` +
`&client_id=${CLIENT_ID}` +
`&redirect_uri=${encodeURIComponent(CALLBACK_URL)}` +
`&scope=api%20refresh_token%20web` +
`&prompt=login%20consent`;
// Show loading state
loginState.classList.add('hidden');
loadingState.classList.remove('hidden');
loadingText.textContent = 'Redirecting to Salesforce...';
loadingSubtext.textContent = 'Please login with your Salesforce credentials';
// Redirect to Salesforce login
window.location.href = authUrl;
}
function handleOAuthResponse() {
const hash = window.location.hash.substring(1);
const params = new URLSearchParams(hash);
if (params.has('access_token')) {
// Get OAuth parameters
accessToken = params.get('access_token');
instanceUrl = params.get('instance_url');
userId = params.get('id').split('/').pop();
orgId = params.get('id').split('/')[4];
// Clean up URL
history.replaceState({}, document.title, window.location.pathname);
// Show loading state
loginState.classList.add('hidden');
loadingState.classList.remove('hidden');
loadingText.textContent = 'Loading org data...';
loadingSubtext.textContent = 'Fetching object metadata from your Salesforce org';
// Fetch user info and start analysis
fetchUserInfo();
} else if (params.has('error')) {
// Handle OAuth error
console.error('OAuth error:', params.get('error_description'));
showLoginState();
}
}
async function fetchUserInfo() {
try {
const response = await fetch(`${instanceUrl}/services/data/v${API_VERSION}/query?q=` +
encodeURIComponent(`SELECT Name, Username, SmallPhotoUrl FROM User WHERE Id = '${userId}'`), {
headers: {
'Authorization': `Bearer ${accessToken}`
}
});
const data = await response.json();
if (data.records && data.records.length > 0) {
const user = data.records[0];
// Update UI with user info
userName.textContent = user.Name;
userPhoto.src = user.SmallPhotoUrl;
orgName.textContent = `Org: ${orgId}`;
userInfo.classList.remove('hidden');
loginButton.classList.add('hidden');
// Start analysis
analyzeOrg();
}
} catch (error) {
console.error('Error fetching user info:', error);
showLoginState();
}
}
// Analysis Functions
async function analyzeOrg() {
try {
loadingText.textContent = 'Analyzing your Salesforce org...';
loadingSubtext.textContent = 'This may take a few moments depending on your org size';
// Get all objects in the org
const objectsResponse = await fetch(`${instanceUrl}/services/data/v${API_VERSION}/sobjects`, {
headers: {
'Authorization': `Bearer ${accessToken}`
}
});
const objects = await objectsResponse.json();
objectsData = objects.sobjects.filter(obj => obj.queryable && !obj.name.endsWith('ChangeEvent'));
totalObjects.textContent = objectsData.length;
// Update progress
updateProgress(0);
// Analyze each object
for (let i = 0; i < objectsData.length; i++) {
const obj = objectsData[i];
await analyzeObject(obj);
updateProgress(((i + 1) / objectsData.length) * 100);
}
// Show results
loadingState.classList.add('hidden');
appContent.classList.remove('hidden');
renderResults();
} catch (error) {
console.error('Error analyzing org:', error);
loadingText.textContent = 'Error analyzing org';
loadingSubtext.textContent = error.message;
}
}
async function analyzeObject(obj) {
try {
// Get object describe
const describeResponse = await fetch(`${instanceUrl}/services/data/v${API_VERSION}/sobjects/${obj.name}/describe`, {
headers: {
'Authorization': `Bearer ${accessToken}`
}
});
const describe = await describeResponse.json();
obj.fields = describe.fields;
// Analyze fields for PII
obj.piiFields = [];
obj.piiScore = 0;
for (const field of obj.fields) {
const piiRisk = detectPII(field);
if (piiRisk.level > 0) {
obj.piiFields.push({
...field,
piiRisk
});
// Add to PII count if high or medium risk
if (piiRisk.level >= 2) {
piiCount++;
piiFields.textContent = piiCount;
}
}
}
// Calculate object PII score (0-100)
if (obj.piiFields.length > 0) {
const highRiskCount = obj.piiFields.filter(f => f.piiRisk.level === 3).length;
const medRiskCount = obj.piiFields.filter(f => f.piiRisk.level === 2).length;
obj.piiScore = Math.min(100, (highRiskCount * 5) + (medRiskCount * 2));
}
} catch (error) {
console.error(`Error analyzing object ${obj.name}:`, error);
obj.error = error.message;
}
}
function detectPII(field) {
const fieldName = field.name.toLowerCase();
const fieldLabel = field.label.toLowerCase();
const fieldType = field.type;
// Common PII patterns
const piiPatterns = {
high: [
'ssn', 'social', 'security', 'taxid', 'tax id', 'creditcard', 'credit card',
'cardnumber', 'card number', 'passport', 'drivinglicense', 'driving license',
'bankaccount', 'bank account', 'routingnumber', 'routing number', 'dob',
'dateofbirth', 'date of birth', 'mothersmaiden', 'mothers maiden'
],
medium: [
'name', 'firstname', 'first name', 'lastname', 'last name', 'middlename',
'middle name', 'email', 'phone', 'mobile', 'address', 'street', 'city',
'state', 'zip', 'postalcode', 'postal code', 'country', 'gender', 'race',
'ethnicity', 'salary', 'income', 'health', 'medical', 'insurance'
]
};
// Check for high risk PII
for (const pattern of piiPatterns.high) {
if (fieldName.includes(pattern) || fieldLabel.includes(pattern)) {
return {
level: 3,
reason: `Field name suggests high risk PII (${pattern})`
};
}
}
// Check for medium risk PII
for (const pattern of piiPatterns.medium) {
if (fieldName.includes(pattern) || fieldLabel.includes(pattern)) {
return {
level: 2,
reason: `Field name suggests potential PII (${pattern})`
};
}
}
// Check field types that might contain PII
if (fieldType === 'email') {
return {
level: 2,
reason: 'Email field type may contain PII'
};
}
if (fieldType === 'phone' || fieldType === 'textarea') {
return {
level: 1,
reason: `${fieldType} field type may contain PII`
};
}
// No PII detected
return {
level: 0,
reason: 'No obvious PII indicators'
};
}
// UI Functions
function updateProgress(percent) {
progressFill.style.width = `${percent}%`;
progressText.textContent = `${Math.round(percent)}%`;
}
function renderResults() {
resultsContainer.innerHTML = '';
// Sort objects by PII score (descending)
const sortedObjects = [...objectsData].sort((a, b) => b.piiScore - a.piiScore);
for (const obj of sortedObjects) {
if (obj.piiFields.length > 0) {
const objectCard = document.createElement('div');
objectCard.className = 'bg-gray-50 rounded-lg border border-gray-200 overflow-hidden slide-in';
// Object header
const header = document.createElement('div');
header.className = 'flex items-center justify-between p-4 bg-white border-b border-gray-200 cursor-pointer';
header.innerHTML = `
<div class="flex items-center">
<div class="w-3 h-3 rounded-full ${getPiiColorClass(obj.piiScore)} mr-3"></div>
<div>
<h4 class="font-medium text-gray-800">${obj.label} (${obj.name})</h4>
<div class="text-xs text-gray-500">${obj.piiFields.length} potential PII fields</div>
</div>
</div>
<div class="flex items-center">
<span class="text-sm font-medium ${getPiiTextColorClass(obj.piiScore)} mr-2">
PII Score: ${obj.piiScore}
</span>
<i class="fas fa-chevron-down text-gray-400 transition-transform"></i>
</div>
`;
// Fields container (initially hidden)
const fieldsContainer = document.createElement('div');
fieldsContainer.className = 'hidden p-4 bg-white';
// Add fields to container
obj.piiFields.sort((a, b) => b.piiRisk.level - a.piiRisk.level).forEach(field => {
const fieldDiv = document.createElement('div');
fieldDiv.className = `p-3 mb-2 rounded-md ${getPiiFieldClass(field.piiRisk.level)}`;
fieldDiv.innerHTML = `
<div class="flex justify-between items-start">
<div>
<div class="font-medium">${field.label} (${field.name})</div>
<div class="text-sm text-gray-600 mt-1">${field.piiRisk.reason}</div>
</div>
<span class="text-xs font-medium px-2 py-1 rounded-full ${getPiiRiskBadgeClass(field.piiRisk.level)}">
${getPiiRiskLabel(field.piiRisk.level)}
</span>
</div>
<div class="text-xs text-gray-500 mt-2">
<span class="font-medium">Type:</span> ${field.type} |
<span class="font-medium">Length:</span> ${field.length || 'N/A'} |
<span class="font-medium">API Name:</span> ${field.name}
</div>
`;
fieldsContainer.appendChild(fieldDiv);
});
// Toggle fields visibility on header click
header.addEventListener('click', () => {
const icon = header.querySelector('i');
fieldsContainer.classList.toggle('hidden');
icon.classList.toggle('fa-chevron-down');
icon.classList.toggle('fa-chevron-up');
});
objectCard.appendChild(header);
objectCard.appendChild(fieldsContainer);
resultsContainer.appendChild(objectCard);
}
}
}
function filterResults() {
const filterValue = filterSelect.value;
const allCards = resultsContainer.querySelectorAll('.bg-gray-50');
allCards.forEach(card => {
const objectName = card.querySelector('h4').textContent;
const isStandard = !objectName.includes('__c');
const hasPII = !card.querySelector('.hidden');
switch (filterValue) {
case 'all':
card.style.display = 'block';
break;
case 'pii':
card.style.display = hasPII ? 'block' : 'none';
break;
case 'standard':
card.style.display = isStandard ? 'block' : 'none';
break;
case 'custom':
card.style.display = isStandard ? 'none' : 'block';
break;
}
});
}
function getPiiColorClass(score) {
if (score >= 60) return 'bg-red-500';
if (score >= 30) return 'bg-yellow-500';
return 'bg-blue-500';
}
function getPiiTextColorClass(score) {
if (score >= 60) return 'text-red-600';
if (score >= 30) return 'text-yellow-600';
return 'text-blue-600';
}
function getPiiFieldClass(level) {
if (level === 3) return 'pii-high';
if (level === 2) return 'pii-medium';
return 'pii-low';
}
function getPiiRiskBadgeClass(level) {
if (level === 3) return 'bg-red-100 text-red-800';
if (level === 2) return 'bg-yellow-100 text-yellow-800';
return 'bg-blue-100 text-blue-800';
}
function getPiiRiskLabel(level) {
if (level === 3) return 'High Risk';
if (level === 2) return 'Medium Risk';
return 'Low Risk';
}
function showLoginState() {
loadingState.classList.add('hidden');
appContent.classList.add('hidden');
loginState.classList.remove('hidden');
loginButton.classList.remove('hidden');
userInfo.classList.add('hidden');
}
function logout() {
// Clear OAuth data
accessToken = null;
instanceUrl = null;
userId = null;
orgId = null;
objectsData = [];
piiCount = 0;
// Reset UI
showLoginState();
totalObjects.textContent = '0';
piiFields.textContent = '0';
progressFill.style.width = '0%';
progressText.textContent = '0%';
}
</script>
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=Naresh1993/deepsitetest" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>