Bugün , acilen bir neden dolayı Firefox'a kayıt şifrelerden bazıları lazım oldu.Bende bir bash shell betiği tasarladım.(şeytanın bile aklına gelmez birşey)
Bu bash shell betiği, firefox 'dan export edilen .cvs dosyasını;
1.Okuyor
2.Komut Çıktısını terminale basıyor.
3.Ardından Dinamik HTML Dosyasına dönüştürüyor.
Böylece eliniz de HTML sayfası şeklinde tüm şifrelerinizin bir yedek dökümü oluyor.
Önce Kod İçeriğini Verelim :
Kod: Tümünü seç
#!/usr/bin/env bash
# =============================================================================
# firefox_csv_export.sh
# Description : Reads a Firefox exported CSV file, displays it in the terminal
# and generates a styled HTML report in the same directory.
# Usage : bash firefox_csv_export.sh
# Compatible : Bash 4+, Linux Mint 22.2 / Ubuntu 24.x
# =============================================================================
set -euo pipefail
# -----------------------------------------------------------------------------
# ANSI Color Codes
# -----------------------------------------------------------------------------
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
CYAN='\033[0;36m'
BLUE='\033[0;34m'
BOLD='\033[1m'
DIM='\033[2m'
RESET='\033[0m'
LINE='\033[0;34m'"$(printf '%0.s-' {1..70})"'\033[0m'
# -----------------------------------------------------------------------------
# Banner
# -----------------------------------------------------------------------------
print_banner() {
echo -e "${CYAN}"
echo " +---------------------------------------------------------+"
echo " | Firefox Password CSV Viewer & Exporter |"
echo " | HTML Report Generator |"
echo " +---------------------------------------------------------+"
echo -e "${RESET}"
}
# -----------------------------------------------------------------------------
# Input: Get and validate CSV file path
# -----------------------------------------------------------------------------
get_csv_path() {
while true; do
echo -e "${BOLD}Enter the full path to your Firefox CSV file:${RESET}"
echo -e "${DIM}Example: /home/linuxmaster/Desktop/firefox_logins.csv${RESET}"
echo -ne "${YELLOW}Path: ${RESET}"
read -r CSV_PATH
# Expand tilde if present
CSV_PATH="${CSV_PATH/#\~/$HOME}"
if [[ -z "$CSV_PATH" ]]; then
echo -e "${RED}[ERROR] Path cannot be empty. Please try again.${RESET}\n"
continue
fi
if [[ ! -f "$CSV_PATH" ]]; then
echo -e "${RED}[ERROR] File not found: ${CSV_PATH}${RESET}\n"
continue
fi
if [[ "${CSV_PATH##*.}" != "csv" ]]; then
echo -e "${YELLOW}[WARNING] File does not have .csv extension. Continue? (y/n): ${RESET}"
read -r CONFIRM
[[ "$CONFIRM" != "y" && "$CONFIRM" != "Y" ]] && continue
fi
break
done
CSV_DIR="$(dirname "$CSV_PATH")"
CSV_BASENAME="$(basename "$CSV_PATH" .csv)"
HTML_OUTPUT="${CSV_DIR}/${CSV_BASENAME}_report.html"
}
# -----------------------------------------------------------------------------
# Parse CSV: skip header, store fields in arrays
# -----------------------------------------------------------------------------
parse_csv() {
echo -e "\n${CYAN}[INFO] Parsing CSV file...${RESET}"
declare -ga URLS=()
declare -ga USERS=()
declare -ga PASSES=()
local line_num=0
while IFS=',' read -r url username password rest; do
((line_num++)) || true
# Skip header row
[[ $line_num -eq 1 ]] && continue
# Skip empty lines
[[ -z "$url" && -z "$username" ]] && continue
# Strip surrounding quotes if present
url="${url//\"/}"
username="${username//\"/}"
password="${password//\"/}"
URLS+=("$url")
USERS+=("$username")
PASSES+=("$password")
done < "$CSV_PATH"
TOTAL_RECORDS=${#URLS[@]}
if [[ $TOTAL_RECORDS -eq 0 ]]; then
echo -e "${RED}[ERROR] No records found in CSV. Check file format.${RESET}"
exit 1
fi
echo -e "${GREEN}[OK] Found ${BOLD}${TOTAL_RECORDS}${RESET}${GREEN} records.${RESET}"
}
# -----------------------------------------------------------------------------
# Terminal Display
# -----------------------------------------------------------------------------
display_terminal() {
echo -e "\n${LINE}"
printf "${BOLD}%-4s %-35s %-25s %-20s${RESET}\n" "No." "URL" "Username" "Password"
echo -e "${LINE}"
for i in "${!URLS[@]}"; do
local num=$((i + 1))
local url="${URLS[$i]}"
local user="${USERS[$i]}"
local pass="${PASSES[$i]}"
# Truncate long values for terminal display
[[ ${#url} -gt 35 ]] && url="${url:0:32}..."
[[ ${#user} -gt 25 ]] && user="${user:0:22}..."
[[ ${#pass} -gt 20 ]] && pass="${pass:0:17}..."
# Alternate row color for readability
if (( num % 2 == 0 )); then
printf "${DIM}%-4s %-35s %-25s %-20s${RESET}\n" "$num" "$url" "$user" "$pass"
else
printf "%-4s %-35s %-25s %-20s\n" "$num" "$url" "$user" "$pass"
fi
done
echo -e "${LINE}"
echo -e "${GREEN}Total: ${BOLD}${TOTAL_RECORDS}${RESET}${GREEN} entries displayed.${RESET}\n"
}
# -----------------------------------------------------------------------------
# HTML Escaping helper
# -----------------------------------------------------------------------------
html_escape() {
local s="$1"
s="${s//&/&}"
s="${s//</<}"
s="${s//>/>}"
s="${s//\"/"}"
s="${s//\'/'}"
echo "$s"
}
# -----------------------------------------------------------------------------
# Generate HTML Report
# -----------------------------------------------------------------------------
generate_html() {
echo -e "${CYAN}[INFO] Generating HTML report...${RESET}"
local GEN_DATE
GEN_DATE="$(date '+%d %B %Y - %H:%M:%S')"
# Build table rows
local TABLE_ROWS=""
for i in "${!URLS[@]}"; do
local num=$((i + 1))
local url
local user
local pass
url="$(html_escape "${URLS[$i]}")"
user="$(html_escape "${USERS[$i]}")"
pass="$(html_escape "${PASSES[$i]}")"
# Extract domain for favicon
local domain=""
if [[ "$url" =~ ^https?://([^/]+) ]]; then
domain="${BASH_REMATCH[1]}"
fi
TABLE_ROWS+="
<tr>
<td class=\"num\">${num}</td>
<td class=\"url-cell\">
$([ -n "$domain" ] && echo "<img class=\"favicon\" src=\"https://www.google.com/s2/favicons?domain=${domain}&sz=16\" alt=\"\" onerror=\"this.style.display='none'\">")
<a href=\"${url}\" target=\"_blank\" rel=\"noopener\">${url}</a>
</td>
<td class=\"user-cell\">
<span class=\"user-badge\">${user}</span>
</td>
<td class=\"pass-cell\">
<span class=\"pass-hidden\" data-pass=\"${pass}\">●●●●●●●●</span>
<button class=\"toggle-btn\" onclick=\"togglePass(this)\">Show</button>
</td>
</tr>"
done
# Write HTML file
cat > "$HTML_OUTPUT" << HTMLEOF
<!DOCTYPE html>
<html lang="tr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Firefox Password Report</title>
<style>
:root {
--bg: #0f1117;
--surface: #1a1d27;
--border: #2a2d3a;
--accent: #e8632a;
--accent2: #f0a500;
--text: #d4d8e8;
--text-dim: #6b7280;
--green: #22c55e;
--blue: #3b82f6;
--row-alt: #161926;
}
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
background: var(--bg);
color: var(--text);
font-family: 'Courier New', monospace;
min-height: 100vh;
padding: 2rem 1rem;
}
header {
max-width: 1100px;
margin: 0 auto 2rem;
border-left: 4px solid var(--accent);
padding-left: 1.2rem;
}
header h1 {
font-size: 1.6rem;
color: #fff;
letter-spacing: 0.05em;
}
header h1 span { color: var(--accent); }
.meta {
margin-top: 0.4rem;
font-size: 0.78rem;
color: var(--text-dim);
}
.meta strong { color: var(--accent2); }
.stats {
max-width: 1100px;
margin: 0 auto 1.5rem;
display: flex;
gap: 1rem;
flex-wrap: wrap;
}
.stat-box {
background: var(--surface);
border: 1px solid var(--border);
border-radius: 6px;
padding: 0.7rem 1.4rem;
font-size: 0.82rem;
}
.stat-box strong {
display: block;
font-size: 1.4rem;
color: var(--accent);
}
.search-bar {
max-width: 1100px;
margin: 0 auto 1.2rem;
}
.search-bar input {
width: 100%;
background: var(--surface);
border: 1px solid var(--border);
border-radius: 6px;
color: var(--text);
padding: 0.6rem 1rem;
font-family: inherit;
font-size: 0.9rem;
outline: none;
transition: border-color 0.2s;
}
.search-bar input:focus { border-color: var(--accent); }
.table-wrap {
max-width: 1100px;
margin: 0 auto;
overflow-x: auto;
border: 1px solid var(--border);
border-radius: 8px;
}
table {
width: 100%;
border-collapse: collapse;
font-size: 0.85rem;
}
thead tr {
background: var(--surface);
border-bottom: 2px solid var(--accent);
}
thead th {
padding: 0.8rem 1rem;
text-align: left;
color: var(--accent);
font-size: 0.75rem;
letter-spacing: 0.1em;
text-transform: uppercase;
white-space: nowrap;
}
tbody tr {
border-bottom: 1px solid var(--border);
transition: background 0.15s;
}
tbody tr:nth-child(even) { background: var(--row-alt); }
tbody tr:hover { background: #1f2335; }
td { padding: 0.65rem 1rem; vertical-align: middle; }
.num {
color: var(--text-dim);
font-size: 0.75rem;
width: 40px;
text-align: center;
}
.url-cell {
max-width: 380px;
overflow: hidden;
}
.url-cell a {
color: var(--blue);
text-decoration: none;
word-break: break-all;
font-size: 0.82rem;
}
.url-cell a:hover { text-decoration: underline; }
.favicon {
width: 14px;
height: 14px;
margin-right: 6px;
vertical-align: middle;
border-radius: 2px;
}
.user-badge {
background: #1e2a1e;
color: var(--green);
border: 1px solid #2d4a2d;
border-radius: 4px;
padding: 0.15rem 0.5rem;
font-size: 0.8rem;
word-break: break-all;
}
.pass-cell { white-space: nowrap; }
.pass-hidden {
font-size: 0.7rem;
color: var(--text-dim);
letter-spacing: 0.2em;
margin-right: 0.5rem;
font-family: inherit;
}
.toggle-btn {
background: var(--surface);
border: 1px solid var(--border);
color: var(--text-dim);
border-radius: 4px;
padding: 0.15rem 0.5rem;
font-size: 0.72rem;
cursor: pointer;
font-family: inherit;
transition: all 0.15s;
}
.toggle-btn:hover {
border-color: var(--accent);
color: var(--accent);
}
footer {
max-width: 1100px;
margin: 2rem auto 0;
text-align: center;
font-size: 0.72rem;
color: var(--text-dim);
border-top: 1px solid var(--border);
padding-top: 1rem;
}
.hidden-row { display: none; }
</style>
</head>
<body>
<header>
<h1>Firefox <span>Password</span> Report</h1>
<div class="meta">
Generated: <strong>${GEN_DATE}</strong> |
Source: <strong>$(html_escape "$CSV_PATH")</strong>
</div>
</header>
<div class="stats">
<div class="stat-box">
<strong>${TOTAL_RECORDS}</strong>
Total Entries
</div>
</div>
<div class="search-bar">
<input type="text" id="searchInput" placeholder="Search by URL or username..." oninput="filterTable()">
</div>
<div class="table-wrap">
<table id="passTable">
<thead>
<tr>
<th>#</th>
<th>URL</th>
<th>Username</th>
<th>Password</th>
</tr>
</thead>
<tbody>
${TABLE_ROWS}
</tbody>
</table>
</div>
<footer>
Firefox Password Report — Generated by firefox_csv_export.sh
| Total: ${TOTAL_RECORDS} entries
</footer>
<script>
function togglePass(btn) {
var span = btn.previousElementSibling;
var stored = span.getAttribute('data-pass');
if (btn.textContent === 'Show') {
span.textContent = stored;
span.style.color = '#e8632a';
span.style.letterSpacing = 'normal';
span.style.fontSize = '0.82rem';
btn.textContent = 'Hide';
} else {
span.innerHTML = '●●●●●●●●';
span.style.color = '';
span.style.letterSpacing = '';
span.style.fontSize = '';
btn.textContent = 'Show';
}
}
function filterTable() {
var q = document.getElementById('searchInput').value.toLowerCase();
var rows = document.querySelectorAll('#passTable tbody tr');
rows.forEach(function(row) {
var text = row.textContent.toLowerCase();
row.classList.toggle('hidden-row', q.length > 0 && text.indexOf(q) === -1);
});
}
</script>
</body>
</html>
HTMLEOF
echo -e "${GREEN}[OK] HTML report saved: ${BOLD}${HTML_OUTPUT}${RESET}"
}
# -----------------------------------------------------------------------------
# Main
# -----------------------------------------------------------------------------
main() {
print_banner
get_csv_path
parse_csv
display_terminal
generate_html
echo -e "\n${BLUE}Done. Open your report in a browser:${RESET}"
echo -e " ${BOLD}xdg-open \"${HTML_OUTPUT}\"${RESET}\n"
}
main "$@"





