Dışa Açık Dijital Varlık ve Maruziyet Takip Formu

Kurumun internete açık alan adları, alt alan adları, IP adresleri, servisleri, uygulamaları, API’leri, uzak erişim noktaları ve benzeri dijital varlıklarını merkezi olarak kayıt altına almak ve bu varlıkların oluşturduğu siber güvenlik maruziyetlerini sistematik biçimde izlemek amacıyla tasarlanmıştır. 

Form; varlık sahipliği, iş kritiklik seviyesi, açık port ve servis bilgileri, zafiyetler, CVE/CVSS değerleri, CISA KEV durumu, aktif istismar bilgisi, kimlik doğrulama ve erişim kontrolleri gibi unsurları bir araya getirerek kurumun dış saldırı yüzeyinin daha görünür, ölçülebilir ve yönetilebilir hâle gelmesini sağlar.

Formun temel hedefi, yalnızca internete açık varlıkların envanterini tutmak değil; tespit edilen risk ve maruziyetlerin sorumlu kişilere atanmasını, önceliklendirilmesini, hedef tarihlerle takip edilmesini, gerekli düzeltmelerin uygulanmasını ve sonrasında yeniden test edilerek doğrulanmasını sağlamaktır. 

Böylece gereksiz internet açıklıklarının kapatılması, kritik varlıkların korunması, risk kabul süreçlerinin kayıt altına alınması ve açık kalan aksiyonların zamanında sonuçlandırılması desteklenir. Bu yapı, kurumun saldırı yüzeyi yönetimi, zafiyet yönetimi ve sürekli siber risk azaltma faaliyetlerini tek bir takip mekanizması altında bütünleştirir.

Bir Not defteri açarak, aşağıdaki (Mavi satırlar) html kodu içine kaydedin. Not defteri uzantısını .txt yerine ".html" olarak değiştirip kaydedin. Ör: "Dışa Açık Dijital Varlık ve Maruziyet Takip Formu.html" sayfayı kapatıp tekrar açtığınızda, web browser üzerinden sayfanız açılacaktır. 

Sol tarafta görünen logo için, html dosyanız nerede ise, masaüstü veya oluşturulan bir klasör. Logonuzda aynı yerde olursa forma yansır. Logo ismi "logo.jpg" olmalı. Çift tıklayarak açtığınızda, browser üzerinde form açılışı gerçekleşir. Veya alanına 5 kez peş peşe tıklayarak çalışma esnasında logo değişimi de yapılabilir.

Başlık, başlığın altındaki, başlığın sağındaki ve alttaki yazıları, not defteri içinde "Ctrl + F" tuşları ile kelime yazarak bulabilir, ilgili metinleri kendinize göre uyarlayarak kaydederek kullanabilirsiniz. Başlık alanındaki "OLGUN BAŞKANLIĞI" alanına 5 kez peş peşe tıklayarak; Departman, Müdürlük veya Başkanlığınıza ait bilgiyi manuel girebilirsiniz.

Formun boş hali;

Formun dolu hali:

PDF / Yazdır

Dışarı Aktarılan Excel Verisi:

HTML Kod:
<!DOCTYPE html>
<html lang="tr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Dışa Açık Dijital Varlık ve Maruziyet Takip Formu</title>
<style>
:root{
  --bg:#f8fafc;--panel:#fff;--line:#d7dee8;--text:#0f172a;--muted:#64748b;
  --blue:#0284c7;--blue-soft:#e0f2fe;--blue-deep:#075985;--green-soft:#dcfce7;--green:#166534;
  --amber-soft:#fef3c7;--amber:#92400e;--red-soft:#fee2e2;--red:#991b1b;--violet-soft:#f3e8ff;--violet:#6b21a8;
  --shadow:0 2px 8px rgba(15,23,42,.06);--radius:6px;
}
*{box-sizing:border-box}
html{scroll-behavior:smooth}
body{margin:0;background:var(--bg);color:var(--text);font-family:"Segoe UI",Arial,sans-serif;font-size:13px;padding:14px}
.app{width:100%;max-width:1540px;margin:0 auto;padding:10px;background:var(--panel);border:1px solid var(--line);border-radius:8px;box-shadow:var(--shadow)}
.top-frame{width:100%;background:#fff;border:1px solid var(--line);border-radius:var(--radius);box-shadow:var(--shadow);overflow:hidden;margin-bottom:10px}
.header{
  width:100%;max-width:none;margin:0;
  display:grid;grid-template-columns:60px minmax(0,1fr) fit-content(45vw);align-items:center;gap:12px;
  background:#fff;border:0;border-left:5px solid var(--blue);padding:10px 14px;
  box-shadow:none
}
.logo-box{width:60px;height:60px;display:flex;align-items:center;justify-content:center;overflow:hidden;cursor:pointer;padding:0}
.logo-box:hover{outline:1px dashed #94a3b8;border-radius:4px}.logo-box:focus-visible{outline:2px solid var(--blue);outline-offset:2px}
.logo-box img{width:100%;height:100%;object-fit:contain;display:block}.logo-fallback{font-weight:700;color:var(--blue);font-size:11px;text-align:center;line-height:1.15}
.headerTitleBlock{min-width:0;align-self:center}.title-main{font-size:21px;font-weight:750;line-height:1.15;color:#0f172a}.title-sub{margin-top:4px;color:var(--muted);font-size:11.5px;line-height:1.35}
.org-title{justify-self:end;align-self:stretch;display:flex;align-items:center;justify-content:flex-end;min-width:0;max-width:45vw;height:100%;text-align:right;color:#000;font-size:15px;font-weight:700;line-height:1.2;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;outline:none}
.org-title.editing{max-width:45vw;min-width:0;margin-left:auto;overflow:hidden;text-overflow:clip;outline:1px dashed var(--blue);background:#f0f9ff;border-radius:3px;padding:2px 4px;cursor:text}
.toolbar{
  display:flex;align-items:center;gap:8px;overflow-x:auto;width:100%;max-width:none;
  margin:0;background:#fff;border:0;border-top:1px solid var(--line);
  box-shadow:none;padding:9px 14px
}
button,.button-like{height:36px;display:inline-flex;flex:0 0 auto;align-items:center;justify-content:center;border:1px solid #cbd5e1;border-radius:6px;padding:8px 12px;background:#f1f5f9;color:#334155;font:700 12px "Segoe UI",Arial,sans-serif;cursor:pointer;white-space:nowrap}
button:hover,.button-like:hover{filter:brightness(.97)}
button.primary,button.excel-export{background:var(--blue-soft);border-color:#bae6fd;color:var(--blue-deep)}
.button-like.excel-import{background:var(--green-soft);border-color:#bbf7d0;color:var(--green)}
button.add{background:#f0f9ff;border-color:#bae6fd;color:#075985}button.danger{background:#fff1f2;border-color:#fecdd3;color:#9f1239;height:30px;padding:5px 9px;font-size:11px}
.toolbar .spacer{flex:1}.statusline{flex:0 0 auto;color:var(--muted);font-size:11px;white-space:nowrap}.hidden{display:none!important}
.section{width:100%;background:#fff;border:1px solid var(--line);border-radius:var(--radius);box-shadow:var(--shadow);margin-bottom:10px;overflow:hidden}
.section-title{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:8px 10px 8px 12px;background:#f8fbfd;border-bottom:1px solid #dbe3ea;border-left:4px solid var(--blue);color:var(--blue-deep);font-weight:700;font-size:13px;line-height:1.2}
.section-title small{font-weight:500;color:#64748b;font-size:11px;text-align:right;line-height:1.25}.section-body{padding:10px}
.grid{display:grid;gap:8px}.g2{grid-template-columns:repeat(2,minmax(0,1fr))}.g3{grid-template-columns:repeat(3,minmax(0,1fr))}.g4{grid-template-columns:repeat(4,minmax(0,1fr))}.g5{grid-template-columns:repeat(5,minmax(0,1fr))}.span2{grid-column:span 2}.span3{grid-column:span 3}.span4{grid-column:span 4}.span5{grid-column:span 5}.field{min-width:0}
label{display:block;font-size:11.5px;font-weight:650;color:#334155;margin:0 0 4px}input,select,textarea{width:100%;border:1px solid #cbd5e1;border-radius:5px;background:#fff;color:#0f172a;padding:7px 8px;font:12.5px "Segoe UI",Arial,sans-serif;outline:none}
input:focus,select:focus,textarea:focus{border-color:#38bdf8;box-shadow:0 0 0 2px rgba(14,165,233,.10)}textarea{min-height:66px;resize:none;overflow:hidden;line-height:1.45}.tall{min-height:94px}.readonly{background:#f8fafc;color:#334155}.hint{font-size:10.5px;color:var(--muted);margin-top:4px;line-height:1.35}
.section-tools{display:flex;align-items:center;gap:8px;margin-bottom:8px}.section-tools .note{color:var(--muted);font-size:10.5px}.section-tools .spacer{flex:1}
.repeat-list{display:grid;gap:8px}.repeat-empty{border:1px dashed #cbd5e1;border-radius:6px;padding:16px;text-align:center;color:#64748b;background:#f8fafc;font-size:11.5px}
.repeat-card{border:1px solid #dbe3ea;border-radius:6px;background:#fff;overflow:hidden;break-inside:avoid}.repeat-head{display:flex;align-items:center;gap:8px;padding:7px 9px;background:#f8fafc;border-bottom:1px solid #e2e8f0}.repeat-title{font-size:12px;font-weight:750;color:#334155}.repeat-code{font-size:11px;font-weight:700;color:#075985;background:#e0f2fe;border:1px solid #bae6fd;border-radius:999px;padding:3px 7px}.repeat-head .spacer{flex:1}.repeat-body{padding:9px}
.kpis{display:grid;grid-template-columns:repeat(5,minmax(0,1fr));gap:8px;margin-bottom:9px}.kpi{border:1px solid #dbe3ea;border-radius:6px;padding:9px;background:#f8fafc;text-align:center}.kpi .v{font-size:20px;font-weight:800;line-height:1.05;color:#0f172a}.kpi .l{margin-top:4px;font-size:10.5px;color:#64748b}.kpi.warn{background:var(--amber-soft);border-color:#fcd34d}.kpi.danger{background:var(--red-soft);border-color:#fecaca}.kpi.good{background:var(--green-soft);border-color:#bbf7d0}.kpi.blue{background:var(--blue-soft);border-color:#bae6fd}
.filterbar{display:grid;grid-template-columns:2fr 1fr 1fr auto;gap:8px;align-items:end}.filter-note{font-size:10.5px;color:var(--muted);margin-top:7px}.muted-card{opacity:.32;display:none}
.summary-box{border:1px solid #bae6fd;background:#f0f9ff;border-radius:6px;padding:9px;margin-top:8px}.summary-box strong{color:#075985}.footer-note{margin:10px 2px 2px;color:#64748b;font-size:10.5px;line-height:1.45;text-align:center}
.req::after{content:" *";color:#b91c1c}.mini-badge{display:inline-flex;align-items:center;border-radius:999px;padding:2px 6px;font-size:10px;font-weight:700;border:1px solid #cbd5e1;background:#fff}.mini-badge.red{background:#fee2e2;border-color:#fecaca;color:#991b1b}.mini-badge.amber{background:#fef3c7;border-color:#fde68a;color:#92400e}.mini-badge.green{background:#dcfce7;border-color:#bbf7d0;color:#166534}
@media(max-width:1100px){body{padding:10px}.app{padding:8px}.header{grid-template-columns:52px minmax(0,1fr) fit-content(40vw);gap:10px}.org-title{max-width:40vw}.org-title.editing{max-width:40vw}.logo-box{width:52px;height:52px}.g4,.g5,.g3{grid-template-columns:repeat(2,minmax(0,1fr))}.kpis{grid-template-columns:repeat(2,minmax(0,1fr))}.filterbar{grid-template-columns:1fr 1fr}.span3,.span4,.span5{grid-column:span 2}}
@media(max-width:680px){body{padding:6px}.app{padding:6px}.header{grid-template-columns:44px minmax(0,1fr) fit-content(34vw);gap:8px;padding:8px 10px}.logo-box{width:44px;height:44px}.org-title,.org-title.editing{max-width:34vw;font-size:12px}.g2,.g3,.g4,.g5,.kpis,.filterbar{grid-template-columns:1fr}.span2,.span3,.span4,.span5{grid-column:span 1}.title-main{font-size:17px}.title-sub{font-size:10.5px}.toolbar{padding:8px 10px}}
@page{size:A4 landscape;margin:8mm 7mm 11mm 7mm;@bottom-right{content:"Sayfa " counter(page) " / " counter(pages);font:8pt "Segoe UI";color:#64748b;text-align:right;white-space:nowrap}}
@media print{
  *{-webkit-print-color-adjust:exact!important;print-color-adjust:exact!important}
  body{background:#fff;font-size:9.5pt;padding:0}.app{max-width:none;padding:2.5mm;border:0.3mm solid #cbd5e1;border-radius:0;box-shadow:none}.top-frame{box-shadow:none;margin-bottom:3mm;border-color:#cbd5e1;border-radius:0}.toolbar,.no-print,.danger,.section-tools button,.filter-section{display:none!important}
  .header{width:100%;margin:0;box-shadow:none;border-radius:0;border-left:1.2mm solid var(--blue);grid-template-columns:14mm minmax(0,1fr) fit-content(65mm);padding:2.5mm 3mm;gap:3mm;align-items:center}.logo-box{width:14mm;height:14mm}.title-main{font-size:14pt}.title-sub{font-size:8pt}.org-title,.org-title.editing{font-size:10pt;max-width:65mm;align-self:stretch;display:flex;align-items:center;justify-content:flex-end;text-align:right;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;padding:0;background:transparent;outline:none}
  .section{box-shadow:none;margin-bottom:1.5mm;border-color:#cbd5e1;overflow:visible;break-inside:auto!important;page-break-inside:auto!important}.section-title{padding:2mm 2.5mm;font-size:9pt;break-after:avoid-page;page-break-after:avoid}.section-title small{font-size:7.5pt}.section-body{padding:2.5mm;break-inside:auto!important;page-break-inside:auto!important}
  .grid{gap:2mm}.g5{grid-template-columns:repeat(5,minmax(0,1fr))}.g4{grid-template-columns:repeat(4,minmax(0,1fr))}.g3{grid-template-columns:repeat(3,minmax(0,1fr))}.g2{grid-template-columns:repeat(2,minmax(0,1fr))}.span2{grid-column:span 2}.span3{grid-column:span 3}.span4{grid-column:span 4}.span5{grid-column:span 5}
  label{font-size:7.5pt;margin-bottom:1mm}input,select,textarea{font-size:8.2pt;padding:1.4mm 1.5mm;border-color:#cbd5e1;min-height:auto}textarea{height:auto!important;min-height:11mm;overflow:visible;white-space:pre-wrap}.tall{min-height:18mm}
  .repeat-card{break-inside:auto!important;page-break-inside:auto!important;margin-bottom:1.5mm;overflow:visible}.repeat-head{padding:1.5mm 2mm;break-after:avoid-page;page-break-after:avoid}.repeat-body{padding:2mm;break-inside:auto!important;page-break-inside:auto!important}.repeat-title,.repeat-code{font-size:7.5pt}.repeat-empty{display:none!important}
  .kpis{grid-template-columns:repeat(5,minmax(0,1fr));gap:2mm}.kpi{padding:2mm;break-inside:avoid-page;page-break-inside:avoid}.kpi .v{font-size:12pt}.kpi .l{font-size:7pt}.footer-note{font-size:7pt;margin-top:1.5mm}.summary-box{break-inside:auto!important;page-break-inside:auto!important}.field{break-inside:auto;page-break-inside:auto}label{break-after:avoid-page;page-break-after:avoid}.muted-card{display:block!important;opacity:1!important}
}
</style>
<script src="https://cdn.sheetjs.com/xlsx-0.20.3/package/dist/xlsx.full.min.js"></script>
</head>
<body>
<div class="app">
<div class="top-frame">
<header class="header">
  <div id="logoBox" class="logo-box" role="button" tabindex="0" aria-label="Logoyu değiştirmek için beş kez tıklayın" title="logo.jpg varsa gösterilir. Değiştirmek için 5 kez art arda tıklayın.">
    <img id="headerLogo" src="logo.jpg" alt="Logo" onerror="this.style.display='none';document.getElementById('logoFallback').style.display='block'">
    <div id="logoFallback" class="logo-fallback" style="display:none">KURUM<br>LOGOSU</div>
  </div>
  <div class="headerTitleBlock">
    <div class="title-main">Dışa Açık Dijital Varlık ve Maruziyet Takip Formu</div>
    <div class="title-sub">(İnternete açık alan adları, IP'ler, servisler, sahiplik, risk, düzeltme ve yeniden test takibi)</div>
  </div>
  <div id="orgTitle" class="org-title" title="Düzenlemek için art arda 5 kez tıklayın">OLGUN BAŞKANLIĞI</div>
</header>

<nav class="toolbar no-print" aria-label="Form işlemleri">
  <button type="button" class="excel-export" onclick="exportExcel()">Excel Dışarı Aktar</button>
  <label class="button-like excel-import" for="excelFile">Excel İçeri Aktar</label>
  <input id="excelFile" type="file" accept=".xlsx,.xls,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,application/vnd.ms-excel" class="hidden" onchange="importExcel(event)">
  <button type="button" onclick="window.print()" title="PDF olarak kaydetmek veya yazdırmak için kullanın.">PDF / Yazdır</button>
  <div class="spacer"></div>
  <div id="saveState" class="statusline">Form hazır</div>
</nav>
</div>
<input id="logoFile" type="file" accept="image/png,image/jpeg,image/webp" class="hidden" onchange="changeLogo(event)">

<section class="section">
  <div class="section-title"><span>1. Envanter / Çalışma Bilgileri</span><small>Tarama veya dış saldırı yüzeyi çalışmasının kimliği ve kapsamı</small></div>
  <div class="section-body">
    <div class="grid g5">
      <div class="field"><label>Çalışma No</label><input id="assessmentNo" placeholder="Örn. EASM-2026-001"></div>
      <div class="field span2"><label>Çalışma / Envanter Adı</label><input id="assessmentName" placeholder="Dış saldırı yüzeyi envanteri"></div>
      <div class="field"><label>Çalışma Tarihi</label><input id="assessmentDate" type="date"></div>
      <div class="field"><label>Durum</label><select id="assessmentStatus"><option value=""></option><option>Taslak</option><option>Devam Ediyor</option><option>Gözden Geçiriliyor</option><option>Tamamlandı</option></select></div>
      <div class="field"><label>Çalışma Sahibi</label><input id="assessmentOwner" placeholder="Ad-soyad / birim"></div>
      <div class="field"><label>Teknik Sorumlu</label><input id="technicalLead" placeholder="Ad-soyad / ekip"></div>
      <div class="field"><label>Tarama / Tespit Kaynağı</label><select id="primarySource"><option value=""></option><option>Kurumsal Tarayıcı</option><option>Shodan</option><option>Censys</option><option>Shadowserver</option><option>DNS Keşfi</option><option>Sertifika Kayıtları</option><option>Zafiyet Tarayıcı</option><option>SIEM</option><option>Tehdit İstihbaratı</option><option>Manuel</option><option>Diğer</option></select></div>
      <div class="field"><label>Sonraki Gözden Geçirme</label><input id="nextReviewDate" type="date"></div>
      <div class="field span5"><label>Kapsam / Hedef Alan</label><textarea id="scope" placeholder="Alan adları, IP blokları, iş birimleri, internet servisleri ve kapsam dışı bırakılan alanlar"></textarea></div>
    </div>
  </div>
</section>

<section class="section">
  <div class="section-title"><span>2. Otomatik Durum Özeti</span><small>Varlık, kritik maruziyet ve aksiyon durumları dinamik hesaplanır</small></div>
  <div class="section-body">
    <div class="kpis">
      <div class="kpi blue"><div class="v" id="kpiAssets">0</div><div class="l">Toplam Dışa Açık Varlık</div></div>
      <div class="kpi danger"><div class="v" id="kpiCritical">0</div><div class="l">Kritik / P1 Maruziyet</div></div>
      <div class="kpi warn"><div class="v" id="kpiOwnerless">0</div><div class="l">Sahibi Belirsiz Varlık</div></div>
      <div class="kpi danger"><div class="v" id="kpiKev">0</div><div class="l">KEV / Aktif İstismar</div></div>
      <div class="kpi warn"><div class="v" id="kpiOverdue">0</div><div class="l">Hedef Tarihi Geçen Aksiyon</div></div>
      <div class="kpi"><div class="v" id="kpiP1P2">0</div><div class="l">Açık P1 / P2</div></div>
      <div class="kpi"><div class="v" id="kpiUnauthorized">0</div><div class="l">Yetkisiz / Plansız Maruziyet</div></div>
      <div class="kpi"><div class="v" id="kpiRiskAccepted">0</div><div class="l">Risk Kabulündeki Kayıt</div></div>
      <div class="kpi warn"><div class="v" id="kpiCert30">0</div><div class="l">30 Gün İçinde Sertifika Bitişi</div></div>
      <div class="kpi good"><div class="v" id="kpiClosed">0</div><div class="l">Kapatılan Maruziyet / Aksiyon</div></div>
    </div>
  </div>
</section>

<section class="section filter-section no-print">
  <div class="section-title"><span>Hızlı Filtre / Arama</span><small>Dinamik kartlarda hızlı arama yapın</small></div>
  <div class="section-body">
    <div class="filterbar">
      <div class="field"><label>Ara</label><input id="quickSearch" placeholder="Alan adı, IP, servis, CVE, sahip, bulgu, aksiyon..." oninput="applyFilters()"></div>
      <div class="field"><label>Kayıt Türü</label><select id="filterCollection" onchange="applyFilters()"><option value="all">Tümü</option><option value="assets">Varlıklar</option><option value="findings">Maruziyet / Bulgular</option><option value="actions">Aksiyonlar</option><option value="exceptions">Risk Kabulü</option><option value="retests">Yeniden Test</option></select></div>
      <div class="field"><label>Kritiklik / Öncelik</label><select id="filterPriority" onchange="applyFilters()"><option value="">Tümü</option><option>Kritik</option><option>Yüksek</option><option>P1 — Acil</option><option>P2 — Yüksek</option><option>Açık</option><option>İşlemde</option><option>Beklemede</option><option>Risk Kabul</option></select></div>
      <button type="button" onclick="clearFilters()">Filtreyi Temizle</button>
    </div>
    <div id="filterCount" class="filter-note">Filtre uygulanmadı.</div>
  </div>
</section>

<section class="section">
  <div class="section-title"><span>3. Dışa Açık Dijital Varlıklar</span><small>Domain, IP, URL, servis, sahiplik, iş bağlamı ve temel internet maruziyeti</small></div>
  <div class="section-body">
    <div class="section-tools no-print"><button class="add" type="button" onclick="addItem('assets')">+ Varlık Ekle</button><span class="note">Her varlığa otomatik kayıt numarası verilir.</span></div>
    <div id="assetsList" class="repeat-list"></div>
  </div>
</section>

<section class="section">
  <div class="section-title"><span>4. Maruziyet / Güvenlik Bulguları</span><small>İnternetten erişilebilir riskleri, teknik kanıtları ve önceliği kaydedin</small></div>
  <div class="section-body">
    <div class="section-tools no-print"><button class="add" type="button" onclick="addItem('findings')">+ Maruziyet / Bulgu Ekle</button><span class="note">Varlık kayıt numarasıyla ilişkilendirin.</span></div>
    <div id="findingsList" class="repeat-list"></div>
  </div>
</section>

<section class="section">
  <div class="section-title"><span>5. Düzeltme / Kapatma Aksiyonları</span><small>Sorumlu, hedef tarih, ilerleme, ticket ve kapanış kanıtını takip edin</small></div>
  <div class="section-body">
    <div class="section-tools no-print"><button class="add" type="button" onclick="addItem('actions')">+ Aksiyon Ekle</button><span class="note">Geciken açık aksiyonlar özet alanında otomatik sayılır.</span></div>
    <div id="actionsList" class="repeat-list"></div>
  </div>
</section>

<section class="section">
  <div class="section-title"><span>6. Risk İstisnası / Risk Kabulü</span><small>Kapatılamayan maruziyetleri gerekçe, risk sahibi, telafi edici kontrol ve süre ile yönetin</small></div>
  <div class="section-body">
    <div class="section-tools no-print"><button class="add" type="button" onclick="addItem('exceptions')">+ Risk Kabul / İstisna Ekle</button></div>
    <div id="exceptionsList" class="repeat-list"></div>
  </div>
</section>

<section class="section">
  <div class="section-title"><span>7. Doğrulama / Yeniden Test</span><small>Düzeltmenin gerçekten maruziyeti ortadan kaldırıp kaldırmadığını doğrulayın</small></div>
  <div class="section-body">
    <div class="section-tools no-print"><button class="add" type="button" onclick="addItem('retests')">+ Yeniden Test Ekle</button></div>
    <div id="retestsList" class="repeat-list"></div>
  </div>
</section>

<section class="section">
  <div class="section-title"><span>8. Kapanış ve Yönetici Özeti</span><small>Genel sonuç, kapanış, sonraki kontrol ve yönetici özeti</small></div>
  <div class="section-body">
    <div class="grid g4">
      <div class="field"><label>Kapanış Durumu</label><select id="closureStatus"><option value=""></option><option>Devam Ediyor</option><option>Maruziyetler Giderildi</option><option>Risk Kabulü ile Kapatıldı</option><option>Kısmen Kapatıldı</option><option>Yanlış Pozitif</option><option>Varlık Devre Dışı</option></select></div>
      <div class="field"><label>Kapanış Tarihi</label><input id="closureDate" type="date"></div>
      <div class="field"><label>Kapatan / Onaylayan</label><input id="closedBy" placeholder="Ad-soyad / makam"></div>
      <div class="field"><label>Bir Sonraki Kontrol Tarihi</label><input id="nextControlDate" type="date"></div>
      <div class="field span2"><label>Kapanış Notu</label><textarea id="closureNote" placeholder="Açık kalan hususlar, kabul edilen riskler ve kapanış koşulları"></textarea></div>
      <div class="field span2"><label>Genel Değerlendirme</label><textarea id="overallAssessment" placeholder="Dış saldırı yüzeyi görünümü, öncelikli riskler ve iyileştirme alanları"></textarea></div>
      <div class="field span4"><label>Yönetici Özeti</label><textarea id="executiveSummary" class="tall" placeholder="İsterseniz aşağıdaki düğme ile kayıtlar üzerinden otomatik özet oluşturun."></textarea><div class="hint no-print"><button type="button" class="primary" onclick="buildExecutiveSummary()">Yönetici Özeti Oluştur</button></div></div>
    </div>
  </div>
</section>

<div class="footer-note">Bu form, dışa açık dijital varlıkların görünürlüğünü, sahipliğini, maruziyetlerini ve düzeltme yaşam döngüsünü tek kayıtta izlemek amacıyla hazırlanmıştır. Yetkisiz test veya müdahale için kullanılmamalıdır.</div>
</div>

<script>
const $=id=>document.getElementById(id);
const fixedFieldIds=[
  "assessmentNo","assessmentName","assessmentDate","assessmentStatus","assessmentOwner","technicalLead","primarySource","nextReviewDate","scope",
  "closureStatus","closureDate","closedBy","nextControlDate","closureNote","overallAssessment","executiveSummary"
];

const specs={
 assets:{prefix:"VAR",title:"Dışa Açık Varlık",sheet:"Varlıklar",fields:[
   {k:"assetName",l:"Varlık Adı",t:"text",c:2,req:true},{k:"assetType",l:"Varlık Türü",t:"select",o:["","Domain","Subdomain","IP","IP Bloğu / CIDR","URL / Web Uygulaması","API","VPN","Uzak Erişim Servisi","E-posta Sistemi","DNS","Bulut Servisi","Güvenlik Cihazı","IoT / OT / ICS","Diğer"]},{k:"environment",l:"Ortam",t:"select",o:["","Prod","Test","Geliştirme","DR","Diğer"]},
   {k:"fqdn",l:"Alan Adı / FQDN",t:"text",c:2},{k:"ip",l:"IP Adresi / CIDR",t:"text"},{k:"url",l:"URL",t:"text",c:2},{k:"port",l:"Port",t:"text"},{k:"protocol",l:"Protokol",t:"text"},{k:"service",l:"Servis",t:"text"},
   {k:"product",l:"Ürün / Teknoloji",t:"text"},{k:"version",l:"Ürün Sürümü",t:"text"},{k:"os",l:"İşletim Sistemi",t:"text"},{k:"dns",l:"DNS Kaydı",t:"text"},{k:"asn",l:"ASN",t:"text"},{k:"provider",l:"Hosting / Bulut Sağlayıcısı",t:"text"},{k:"location",l:"Fiziksel / Mantıksal Konum",t:"text"},
   {k:"businessUnit",l:"İlgili Birim / Departman",t:"text"},{k:"businessOwner",l:"İş Sahibi",t:"text",req:true},{k:"technicalOwner",l:"Teknik Sorumlu",t:"text"},{k:"systemOwner",l:"Sistem / Uygulama Sorumlusu",t:"text"},{k:"securityOwner",l:"Güvenlik Sorumlusu",t:"text"},{k:"supplier",l:"Tedarikçi / Hizmet Sağlayıcı",t:"text"},
   {k:"criticality",l:"Varlık Kritiklik Seviyesi",t:"select",req:true,o:["","Kritik","Yüksek","Orta","Düşük"]},{k:"dataClass",l:"Veri Gizlilik Sınıfı",t:"select",o:["","Çok Gizli / Kritik","Gizli","Kurum İçi","Herkese Açık","Diğer"]},{k:"businessImpact",l:"İş Sürecine Etkisi",t:"textarea",c:2},{k:"inventoryNo",l:"CMDB / Envanter Kayıt No",t:"text"},{k:"inInventory",l:"Kurum Envanterinde Mevcut mu?",t:"select",o:["","Evet","Hayır","Doğrulanamadı"]},
   {k:"internetRequired",l:"İnternete Açık Olması Gerekli mi?",t:"select",req:true,o:["","Evet","Hayır","Değerlendiriliyor"]},{k:"businessJustification",l:"İş Gereksinimi / Maruziyet Gerekçesi",t:"textarea",c:2},{k:"exposureAuthorized",l:"Maruziyet Yetkili mi?",t:"select",o:["","Evet","Hayır","Doğrulanamadı"]},{k:"exposurePlan",l:"Maruziyet Türü",t:"select",o:["","Planlı","Plansız","Geçici","Doğrulanamadı"]},
   {k:"adminOpen",l:"Yönetim Arayüzü İnternete Açık mı?",t:"select",o:["","Evet","Hayır","Bilinmiyor"]},{k:"authentication",l:"Kimlik Doğrulama",t:"select",o:["","Var","Yok","Bilinmiyor"]},{k:"mfa",l:"MFA",t:"select",o:["","Aktif","Aktif Değil","Uygulanamaz","Bilinmiyor"]},{k:"vpnJump",l:"VPN / Jump Host",t:"select",o:["","Var","Yok","Uygulanamaz","Bilinmiyor"]},{k:"ipRestriction",l:"IP Kısıtlaması",t:"select",o:["","Var","Yok","Uygulanamaz","Bilinmiyor"]},{k:"waf",l:"WAF / Reverse Proxy",t:"select",o:["","Var","Yok","Uygulanamaz","Bilinmiyor"]},{k:"acl",l:"ACL ile Sınırlandırılmış mı?",t:"select",o:["","Evet","Hayır","Uygulanamaz","Bilinmiyor"]},
   {k:"tls",l:"TLS / HTTPS",t:"select",o:["","Aktif","Aktif Değil","Uygulanamaz","Bilinmiyor"]},{k:"certStatus",l:"Sertifika Durumu",t:"select",o:["","Geçerli","Yakında Sona Erecek","Süresi Dolmuş","Geçersiz / Hatalı","Uygulanamaz","Bilinmiyor"]},{k:"certExpiry",l:"Sertifika Bitiş Tarihi",t:"date"},{k:"firstSeen",l:"İlk Görülme",t:"datetime-local"},{k:"lastSeen",l:"Son Görülme",t:"datetime-local"},{k:"discoverySource",l:"Tespit Kaynağı",t:"text"},
   {k:"banner",l:"Banner / HTTP / TLS Bilgisi",t:"textarea",c:2},{k:"openPorts",l:"Açık Portlar / Servisler",t:"textarea",c:2},{k:"notes",l:"Varlık Notları",t:"textarea",c:4}
 ]},
 findings:{prefix:"MAR",title:"Maruziyet / Bulgu",sheet:"Maruziyetler",fields:[
   {k:"assetRef",l:"İlgili Varlık No",t:"text",req:true},{k:"title",l:"Bulgu Başlığı",t:"text",c:2,req:true},{k:"exposureType",l:"Maruziyet Türü",t:"select",o:["","Gereksiz Açık Port","Yönetim Paneli","RDP","SSH","VPN","Veritabanı","API","Dosya Paylaşımı","Açık Dizin","Varsayılan Sayfa","Varsayılan Kimlik Bilgileri","Güncel Olmayan Yazılım","Zayıf TLS","Bilgi Sızıntısı","Yanlış Yapılandırma","Debug / Test Servisi","Diğer"]},
   {k:"affectedService",l:"Etkilenen Servis",t:"text"},{k:"port",l:"Etkilenen Port",t:"text"},{k:"cve",l:"CVE",t:"text"},{k:"cwe",l:"CWE",t:"text"},{k:"cvss",l:"CVSS",t:"number",min:0,max:10,step:"0.1"},{k:"kev",l:"CISA KEV",t:"select",req:true,o:["","Evet","Hayır","Doğrulanmadı"]},{k:"activeExploit",l:"Aktif İstismar",t:"select",req:true,o:["","Evet","Hayır","Bilinmiyor"]},{k:"poc",l:"Exploit / PoC",t:"select",o:["","Mevcut","Mevcut Değil","Bilinmiyor"]},
   {k:"sensitiveData",l:"Hassas Bilgi Açığa Çıkıyor mu?",t:"select",o:["","Evet","Hayır","Bilinmiyor"]},{k:"unauthorizedAccess",l:"Yetkisiz Erişim Mümkün mü?",t:"select",o:["","Evet","Hayır","Bilinmiyor"]},{k:"severity",l:"Maruziyet Seviyesi",t:"select",o:["","Kritik","Yüksek","Orta","Düşük","Bilgi"]},{k:"likelihood",l:"Olasılık",t:"select",o:["","Çok Yüksek","Yüksek","Orta","Düşük","Çok Düşük"]},{k:"impact",l:"Etki",t:"select",o:["","Çok Yüksek","Yüksek","Orta","Düşük","Çok Düşük"]},{k:"priority",l:"Öncelik",t:"select",req:true,o:["","P1 — Acil","P2 — Yüksek","P3 — Orta","P4 — Düşük"]},
   {k:"description",l:"Bulgu Açıklaması",t:"textarea",c:2},{k:"technicalDetail",l:"Teknik Detay",t:"textarea",c:2},{k:"evidence",l:"Kanıt / Teknik Çıktı",t:"textarea",c:2},{k:"recommendation",l:"Önerilen İşlem",t:"textarea",c:2}
 ]},
 actions:{prefix:"AKS",title:"Düzeltme Aksiyonu",sheet:"Aksiyonlar",fields:[
   {k:"findingRef",l:"İlgili Bulgu No",t:"text",req:true},{k:"assetRef",l:"İlgili Varlık No",t:"text"},{k:"actionType",l:"Önerilen İşlem",t:"select",o:["","Servisi Kapat","Portu Kapat","İnternet Erişimini Kaldır","VPN Arkasına Al","IP Kısıtlaması Yap","MFA Etkinleştir","WAF Arkasına Al","Güncelle / Patch","Versiyon Yükselt","Yapılandırmayı Düzelt","Sertifikayı Yenile","İzlemeye Al","Risk Kabulü","Diğer"]},{k:"priority",l:"Öncelik",t:"select",o:["","P1 — Acil","P2 — Yüksek","P3 — Orta","P4 — Düşük"]},
   {k:"owner",l:"Aksiyon Sahibi",t:"text",req:true},{k:"unit",l:"Sorumlu Birim",t:"text"},{k:"assignee",l:"Atanan Personel",t:"text"},{k:"openDate",l:"Açılış Tarihi",t:"date"},{k:"targetDate",l:"Hedef Tarih",t:"date",req:true},{k:"sla",l:"SLA",t:"text"},{k:"completionDate",l:"Tamamlanma Tarihi",t:"date"},{k:"ticket",l:"Ticket / Change No",t:"text"},{k:"progress",l:"İlerleme (%)",t:"number",min:0,max:100},{k:"status",l:"Aksiyon Durumu",t:"select",o:["","Açık","Atandı","İşlemde","Beklemede","Doğrulama Bekliyor","Risk Kabul","Kapatıldı","İptal"]},
   {k:"description",l:"Aksiyon Açıklaması",t:"textarea",c:2},{k:"closureEvidence",l:"Kapanış Kanıtı",t:"textarea",c:2},{k:"delayReason",l:"Gecikme / Bekleme Nedeni",t:"textarea",c:2},{k:"notes",l:"Aksiyon Notu",t:"textarea",c:2}
 ]},
 exceptions:{prefix:"RSK",title:"Risk Kabul / İstisna",sheet:"Risk Kabulü",fields:[
   {k:"findingRef",l:"İlgili Bulgu No",t:"text"},{k:"assetRef",l:"İlgili Varlık No",t:"text"},{k:"exceptionNo",l:"Risk İstisna No",t:"text"},{k:"cannotClose",l:"Maruziyet Kapatılamıyor mu?",t:"select",o:["","Evet","Hayır"]},{k:"riskAccepted",l:"Risk Kabul Ediliyor mu?",t:"select",o:["","Evet","Hayır","Onay Bekliyor"]},{k:"riskOwner",l:"Risk Sahibi",t:"text",req:true},{k:"approvedBy",l:"Onaylayan",t:"text"},
   {k:"startDate",l:"Başlangıç Tarihi",t:"date"},{k:"endDate",l:"Bitiş Tarihi",t:"date",req:true},{k:"reviewDate",l:"Yeniden Değerlendirme Tarihi",t:"date"},{k:"reason",l:"Kapatılamama Gerekçesi",t:"textarea",c:2},{k:"businessNeed",l:"İş Gereksinimi",t:"textarea",c:2},{k:"compensatingControls",l:"Telafi Edici Kontroller",t:"textarea",c:2},{k:"note",l:"Açıklama / Not",t:"textarea",c:2}
 ]},
 retests:{prefix:"TST",title:"Yeniden Test",sheet:"Yeniden Test",fields:[
   {k:"assetRef",l:"İlgili Varlık No",t:"text"},{k:"findingRef",l:"İlgili Bulgu No",t:"text",req:true},{k:"actionRef",l:"İlgili Aksiyon No",t:"text"},{k:"fixApplied",l:"Düzeltme Uygulandı mı?",t:"select",o:["","Evet","Hayır","Kısmen"]},{k:"testDate",l:"Yeniden Test Tarihi",t:"date",req:true},{k:"tester",l:"Yeniden Test Eden",t:"text"},
   {k:"portOpen",l:"Port Hâlâ Açık mı?",t:"select",o:["","Evet","Hayır","Uygulanamaz"]},{k:"serviceOpen",l:"Servis Hâlâ İnternete Açık mı?",t:"select",o:["","Evet","Hayır","Uygulanamaz"]},{k:"vulnerabilityContinues",l:"Zafiyet Devam Ediyor mu?",t:"select",o:["","Evet","Hayır","Uygulanamaz"]},{k:"exposureResolved",l:"Maruziyet Giderildi mi?",t:"select",req:true,o:["","Evet","Hayır","Kısmen"]},{k:"result",l:"Doğrulama Sonucu",t:"select",o:["","Başarılı","Başarısız","Kısmen Başarılı"]},
   {k:"scanResult",l:"Yeniden Tarama Sonucu",t:"textarea",c:2},{k:"evidence",l:"Doğrulama Kanıtı",t:"textarea",c:2},{k:"note",l:"Doğrulama Notu",t:"textarea",c:4}
 ]}
};

const counters={assets:0,findings:0,actions:0,exceptions:0,retests:0};
function escapeHtml(v){return String(v??"").replace(/[&<>'"]/g,c=>({"&":"&amp;","<":"&lt;",">":"&gt;","'":"&#39;",'"':"&quot;"}[c]));}
function newCode(name){counters[name]++;return `${specs[name].prefix}-${String(counters[name]).padStart(3,"0")}`;}
function adjustCounterFromCode(name,code){const n=parseInt(String(code||"").split("-").pop(),10);if(Number.isFinite(n)&&n>counters[name])counters[name]=n;}
function fieldHtml(f,value=""){
  const span=f.c||1,cls=`field${span>1?` span${span}`:""}`,val=value??"",req=f.req?" req":"";
  if(f.t==="textarea")return `<div class="${cls}"><label class="${req}">${f.l}</label><textarea data-key="${f.k}">${escapeHtml(val)}</textarea></div>`;
  if(f.t==="select")return `<div class="${cls}"><label class="${req}">${f.l}</label><select data-key="${f.k}">${f.o.map(o=>`<option${String(o)===String(val)?" selected":""}>${escapeHtml(o)}</option>`).join("")}</select></div>`;
  const min=f.min!==undefined?` min="${f.min}"`:"",max=f.max!==undefined?` max="${f.max}"`:"",step=f.step!==undefined?` step="${f.step}"`:"";
  return `<div class="${cls}"><label class="${req}">${f.l}</label><input data-key="${f.k}" type="${f.t||"text"}" value="${escapeHtml(val)}"${min}${max}${step}></div>`;
}
function addItem(name,data={}){
  const spec=specs[name],container=$(name+"List");if(!spec||!container)return;
  container.querySelector(".repeat-empty")?.remove();
  const code=data.code||newCode(name);adjustCounterFromCode(name,code);
  const card=document.createElement("div");card.className="repeat-card";card.dataset.collection=name;card.dataset.code=code;
  card.innerHTML=`<div class="repeat-head"><span class="repeat-title">${spec.title}</span><span class="repeat-code">${escapeHtml(code)}</span><span class="cardFlags"></span><div class="spacer"></div><button type="button" class="danger no-print" onclick="removeItem(this)">Kaldır</button></div><div class="repeat-body"><div class="grid g4">${spec.fields.map(f=>fieldHtml(f,data[f.k])).join("")}</div></div>`;
  container.appendChild(card);card.querySelectorAll("textarea").forEach(autoGrow);updateCardFlags(card);markChanged();updateKpis();applyFilters();return card;
}
function removeItem(btn){const card=btn.closest(".repeat-card"),name=card?.dataset.collection;if(!card)return;card.remove();ensureEmpty(name);markChanged();updateKpis();applyFilters();}
function ensureEmpty(name){const c=$(name+"List");if(c&&!c.querySelector(".repeat-card"))c.innerHTML=`<div class="repeat-empty">Henüz kayıt eklenmedi.</div>`;}
function collectCollection(name){return [...$(name+"List").querySelectorAll(".repeat-card")].map(card=>{const row={code:card.dataset.code};card.querySelectorAll("[data-key]").forEach(el=>row[el.dataset.key]=el.value);return row;});}
function clearCollection(name){$(name+"List").innerHTML="";counters[name]=0;ensureEmpty(name);}
function applyCollection(name,rows){clearCollection(name);(rows||[]).forEach(r=>addItem(name,r));ensureEmpty(name);}
function autoGrow(el){if(!el||el.tagName!=="TEXTAREA")return;el.style.height="auto";el.style.height=Math.max(el.scrollHeight,66)+"px";}
function markChanged(){if($("saveState"))$("saveState").textContent="Kaydedilmemiş değişiklikler";}
function todayISO(){const d=new Date(),off=d.getTimezoneOffset();return new Date(d.getTime()-off*60000).toISOString().slice(0,10);}
function daysUntil(dateStr){if(!dateStr)return null;const a=new Date(todayISO()+"T00:00:00"),b=new Date(dateStr+"T00:00:00");if(Number.isNaN(b.getTime()))return null;return Math.ceil((b-a)/86400000);}
function filledRows(name){return collectCollection(name).filter(r=>Object.entries(r).some(([k,v])=>k!=="code"&&String(v||"").trim()));}
function updateKpis(){
  const assets=filledRows("assets"),findings=filledRows("findings"),actions=filledRows("actions"),exceptions=filledRows("exceptions"),retests=filledRows("retests");
  const critical=findings.filter(r=>r.severity==="Kritik"||r.priority==="P1 — Acil").length;
  const ownerless=assets.filter(r=>!String(r.businessOwner||"").trim()).length;
  const kev=findings.filter(r=>r.kev==="Evet"||r.activeExploit==="Evet").length;
  const activeActions=actions.filter(r=>!["Kapatıldı","İptal"].includes(r.status));
  const overdue=activeActions.filter(r=>r.targetDate&&r.targetDate<todayISO()).length;
  const p1p2=findings.filter(r=>["P1 — Acil","P2 — Yüksek"].includes(r.priority)).length;
  const unauthorized=assets.filter(r=>r.exposureAuthorized==="Hayır"||r.exposurePlan==="Plansız").length;
  const riskAccepted=exceptions.filter(r=>r.riskAccepted==="Evet").length+actions.filter(r=>r.status==="Risk Kabul").length;
  const cert30=assets.filter(r=>{const d=daysUntil(r.certExpiry);return d!==null&&d>=0&&d<=30;}).length;
  const resolvedFindingRefs=new Set(retests.filter(r=>r.exposureResolved==="Evet"||r.result==="Başarılı").map(r=>r.findingRef).filter(Boolean));
  const closedActions=actions.filter(r=>r.status==="Kapatıldı").length;
  $("kpiAssets").textContent=assets.length;$("kpiCritical").textContent=critical;$("kpiOwnerless").textContent=ownerless;$("kpiKev").textContent=kev;$("kpiOverdue").textContent=overdue;
  $("kpiP1P2").textContent=p1p2;$("kpiUnauthorized").textContent=unauthorized;$("kpiRiskAccepted").textContent=riskAccepted;$("kpiCert30").textContent=cert30;$("kpiClosed").textContent=resolvedFindingRefs.size+closedActions;
  document.querySelectorAll(".repeat-card").forEach(updateCardFlags);
}
function updateCardFlags(card){
  if(!card)return;const name=card.dataset.collection,row={};card.querySelectorAll("[data-key]").forEach(el=>row[el.dataset.key]=el.value);const flags=[];
  if(name==="assets"){
    if(!row.businessOwner)flags.push('<span class="mini-badge amber">Sahipsiz</span>');
    if(row.exposureAuthorized==="Hayır"||row.exposurePlan==="Plansız")flags.push('<span class="mini-badge red">Yetkisiz / Plansız</span>');
    const d=daysUntil(row.certExpiry);if(d!==null&&d>=0&&d<=30)flags.push('<span class="mini-badge amber">Sertifika ≤30 gün</span>');
  }
  if(name==="findings"){
    if(row.priority==="P1 — Acil"||row.severity==="Kritik")flags.push('<span class="mini-badge red">Kritik</span>');
    if(row.kev==="Evet")flags.push('<span class="mini-badge red">KEV</span>');
    if(row.activeExploit==="Evet")flags.push('<span class="mini-badge red">Aktif İstismar</span>');
  }
  if(name==="actions"){
    if(row.status==="Kapatıldı")flags.push('<span class="mini-badge green">Kapatıldı</span>');
    else if(row.targetDate&&row.targetDate<todayISO())flags.push('<span class="mini-badge red">Gecikmiş</span>');
  }
  if(name==="exceptions"&&row.riskAccepted==="Evet")flags.push('<span class="mini-badge amber">Risk Kabulü</span>');
  if(name==="retests"&&row.exposureResolved==="Evet")flags.push('<span class="mini-badge green">Doğrulandı</span>');
  const box=card.querySelector(".cardFlags");if(box)box.innerHTML=flags.join(" ");
}
function applyFilters(){
  const q=($("quickSearch")?.value||"").trim().toLocaleLowerCase("tr-TR"),collection=$("filterCollection")?.value||"all",priority=$("filterPriority")?.value||"";let shown=0,total=0;
  document.querySelectorAll(".repeat-card").forEach(card=>{total++;const typeOk=collection==="all"||card.dataset.collection===collection;const text=card.innerText.toLocaleLowerCase("tr-TR");const qOk=!q||text.includes(q);const pOk=!priority||text.includes(priority.toLocaleLowerCase("tr-TR"));const ok=typeOk&&qOk&&pOk;card.classList.toggle("muted-card",!ok);if(ok)shown++;});
  if($("filterCount"))$("filterCount").textContent=(q||collection!=="all"||priority)?`${shown} / ${total} dinamik kayıt gösteriliyor.`:"Filtre uygulanmadı.";
}
function clearFilters(){if($("quickSearch"))$("quickSearch").value="";if($("filterCollection"))$("filterCollection").value="all";if($("filterPriority"))$("filterPriority").value="";applyFilters();}
function buildExecutiveSummary(){
  const assets=filledRows("assets"),findings=filledRows("findings"),actions=filledRows("actions"),exceptions=filledRows("exceptions"),retests=filledRows("retests");
  const critical=findings.filter(r=>r.severity==="Kritik"||r.priority==="P1 — Acil").length,high=findings.filter(r=>r.severity==="Yüksek"||r.priority==="P2 — Yüksek").length;
  const kev=findings.filter(r=>r.kev==="Evet").length,activeExploit=findings.filter(r=>r.activeExploit==="Evet").length,ownerless=assets.filter(r=>!r.businessOwner).length;
  const open=actions.filter(r=>!["Kapatıldı","İptal"].includes(r.status)).length,overdue=actions.filter(r=>!["Kapatıldı","İptal"].includes(r.status)&&r.targetDate&&r.targetDate<todayISO()).length;
  const resolved=retests.filter(r=>r.exposureResolved==="Evet"||r.result==="Başarılı").length,riskAccepted=exceptions.filter(r=>r.riskAccepted==="Evet").length;
  const parts=[];if($("assessmentName").value)parts.push(`Çalışma: ${$("assessmentName").value}${$("assessmentNo").value?` (${$("assessmentNo").value})`:""}.`);
  parts.push(`Toplam ${assets.length} dışa açık dijital varlık kayıt altına alındı; ${ownerless} varlığın iş sahibi alanı boş.`);
  parts.push(`${findings.length} maruziyet/bulgu kaydedildi; ${critical} kritik/P1, ${high} yüksek/P2, ${kev} KEV ve ${activeExploit} aktif istismar işaretli kayıt bulunuyor.`);
  parts.push(`${actions.length} düzeltme aksiyonu tanımlandı; ${open} açık/devam eden, ${overdue} hedef tarihi geçmiş aksiyon bulunuyor.`);
  if(exceptions.length)parts.push(`${exceptions.length} risk istisnası/kabul kaydı mevcut; ${riskAccepted} adedi kabul edilmiş risk olarak işaretli.`);
  if(retests.length)parts.push(`${retests.length} yeniden test kaydının ${resolved} adedinde maruziyet giderildi/başarılı sonucu bulunuyor.`);
  if($("overallAssessment").value)parts.push(`Genel değerlendirme: ${$("overallAssessment").value}`);
  $("executiveSummary").value=parts.join("\n\n");autoGrow($("executiveSummary"));markChanged();
}

document.addEventListener("input",e=>{if(e.target.tagName==="TEXTAREA")autoGrow(e.target);if(e.target.closest(".repeat-card")){updateCardFlags(e.target.closest(".repeat-card"));updateKpis();}markChanged();});
document.addEventListener("change",e=>{if(e.target.closest(".repeat-card")){updateCardFlags(e.target.closest(".repeat-card"));updateKpis();}markChanged();});

function fieldCategory(el){const section=el.closest(".section"),title=section?.querySelector(".section-title span");return title?title.textContent.trim():"Genel";}
function fieldLabel(el){const field=el.closest(".field"),label=field?.querySelector("label");return label?label.textContent.replace(" *","").trim():(el.id||"Alan");}
function collectFixed(){const d={};fixedFieldIds.forEach(id=>{const el=$(id);if(el)d[id]=el.value});d.orgTitle=$("orgTitle").textContent.trim();return d;}
function applyFixed(data={}){fixedFieldIds.forEach(id=>{if(data[id]!==undefined&&$(id))$(id).value=data[id]??""});if(data.orgTitle)$("orgTitle").textContent=data.orgTitle;document.querySelectorAll("textarea").forEach(autoGrow);}
function ensureExcelLibrary(){if(typeof XLSX==="undefined"){alert("Excel kütüphanesi yüklenemedi. Excel içe/dışa aktarma için internet bağlantısını kontrol edip sayfayı yeniden açın.");return false}return true;}
function setSheetPrint(ws){ws["!pageSetup"]={orientation:"landscape",paperSize:9,fitToWidth:1,fitToHeight:0};ws["!margins"]={left:0.25,right:0.25,top:0.35,bottom:0.45,header:0.15,footer:0.2};}
function sheetFromRows(rows,widths){const ws=XLSX.utils.aoa_to_sheet(rows);ws["!cols"]=widths.map(w=>({wch:w}));if(rows.length>1)ws["!autofilter"]={ref:`A1:${XLSX.utils.encode_col(rows[0].length-1)}${rows.length}`};setSheetPrint(ws);return ws;}
function exportExcel(){
  if(!ensureExcelLibrary())return;const wb=XLSX.utils.book_new(),general=[["Kategori","Alan ID","Alan","Değer"],["Başlık","orgTitle","Kurum / Başkanlık Başlığı",$("orgTitle").textContent.trim()]];
  fixedFieldIds.forEach(id=>{const el=$(id);if(el)general.push([fieldCategory(el),id,fieldLabel(el),el.value])});XLSX.utils.book_append_sheet(wb,sheetFromRows(general,[38,28,42,90]),"Genel Form");
  Object.entries(specs).forEach(([name,spec])=>{const data=collectCollection(name),heads=["Kayıt No",...spec.fields.map(f=>f.l)],rows=[heads,...data.map(r=>[r.code,...spec.fields.map(f=>r[f.k]??"")])];XLSX.utils.book_append_sheet(wb,sheetFromRows(rows,[14,...spec.fields.map(f=>f.t==="textarea"?42:22)]),spec.sheet.slice(0,31));});
  XLSX.writeFile(wb,"Dışa Açık Dijital Varlık ve Maruziyet Takip.xlsx");$("saveState").textContent="Excel dosyası dışarı aktarıldı";
}
function excelCell(v){if(v===null||v===undefined)return"";if(v instanceof Date){const off=v.getTimezoneOffset();return new Date(v.getTime()-off*60000).toISOString().slice(0,16)}return String(v).trim();}
function parseDynamicSheet(ws,spec){if(!ws)return[];const rows=XLSX.utils.sheet_to_json(ws,{defval:"",raw:false});return rows.map(row=>{const o={code:excelCell(row["Kayıt No"])};spec.fields.forEach(f=>o[f.k]=excelCell(row[f.l]));return o}).filter(r=>r.code||spec.fields.some(f=>r[f.k]));}
function importExcel(e){
  const file=e.target.files&&e.target.files[0];if(!file)return;if(!ensureExcelLibrary()){e.target.value="";return}const reader=new FileReader();
  reader.onload=ev=>{try{const wb=XLSX.read(new Uint8Array(ev.target.result),{type:"array",cellDates:true}),generalName=wb.SheetNames.includes("Genel Form")?"Genel Form":wb.SheetNames[0],ws=wb.Sheets[generalName],rows=XLSX.utils.sheet_to_json(ws,{defval:"",raw:true}),fixed={};
    rows.forEach(row=>{const id=excelCell(row["Alan ID"]),value=row["Değer"];if(id==="orgTitle")fixed.orgTitle=excelCell(value);else if(fixedFieldIds.includes(id))fixed[id]=excelCell(value)});applyFixed(fixed);
    Object.entries(specs).forEach(([name,spec])=>{const sname=wb.SheetNames.find(n=>n===spec.sheet.slice(0,31));if(sname)applyCollection(name,parseDynamicSheet(wb.Sheets[sname],spec));else clearCollection(name)});updateKpis();applyFilters();$("saveState").textContent="Excel dosyası içeri aktarıldı";
  }catch(err){console.error(err);alert("Excel dosyası okunamadı. Bu formdan dışarı aktarılan .xlsx/.xls yapısını kullanın.");$("saveState").textContent="Excel içeri aktarma başarısız"}finally{e.target.value=""}};reader.readAsArrayBuffer(file);
}
function bindFiveClick(target,action){let count=0,timer=null;target.addEventListener("click",()=>{count++;clearTimeout(timer);timer=setTimeout(()=>count=0,2500);if(count>=5){count=0;clearTimeout(timer);action()}})}
bindFiveClick($("orgTitle"),()=>{$("orgTitle").contentEditable="true";$("orgTitle").classList.add("editing");$("orgTitle").focus();$("saveState").textContent="Kurum başlığı düzenleme açık"});
$("orgTitle").addEventListener("blur",()=>{$("orgTitle").contentEditable="false";$("orgTitle").classList.remove("editing")});$("orgTitle").addEventListener("keydown",e=>{if(e.key==="Enter"){e.preventDefault();$("orgTitle").blur()}});
bindFiveClick($("logoBox"),()=>{$("logoFile").click();$("saveState").textContent="Logo seçimi açıldı"});$("logoBox").addEventListener("keydown",e=>{if(e.key==="Enter"||e.key===" "){e.preventDefault();$("logoBox").click()}});
function changeLogo(e){const file=e.target.files&&e.target.files[0];if(!file)return;if(!file.type.startsWith("image/")){alert("Lütfen PNG, JPG/JPEG veya WEBP türünde bir logo seçin.");e.target.value="";return}const reader=new FileReader();reader.onload=()=>{const img=$("headerLogo");img.src=reader.result;img.style.display="block";$("logoFallback").style.display="none";try{localStorage.setItem("olgunHeaderLogo",reader.result)}catch(err){}$("saveState").textContent="Logo güncellendi"};reader.readAsDataURL(file);e.target.value="";}
try{const savedLogo=localStorage.getItem("olgunHeaderLogo");if(savedLogo){$("headerLogo").src=savedLogo;$("headerLogo").style.display="block";$("logoFallback").style.display="none"}}catch(err){}
Object.keys(specs).forEach(ensureEmpty);document.querySelectorAll("textarea").forEach(autoGrow);updateKpis();applyFilters();$("saveState").textContent="Form hazır";
</script>
</body>
</html>