Translated ['', 'src/welcome/hacktricks-values-and-faq.md'] to ko

This commit is contained in:
Translator 2025-10-04 09:05:15 +00:00
parent 7518eb29ef
commit 7237839a94
2 changed files with 254 additions and 217 deletions

View File

@ -5,17 +5,17 @@
## HackTricks 가치
> [!TIP]
> 다음은 **HackTricks 프로젝트의 가치**입니다:
> 다음은 HackTricks 프로젝트의 **가치**입니다:
>
> - 모든 인터넷 사용자에게 **FREE**로 **EDUCATIONAL hacking** 리소스에 접근 권한을 제공합니다.
> - 모든 인터넷 사용자에게 **교육용 hacking** 리소스에 **무료**로 접근을 제공합니다.
> - Hacking은 학습과 관련이 있으며, 학습은 가능한 한 무료여야 합니다.
> - 이 책의 목적은 포괄적인 **교육 자료**로서 제공하는 것입니다.
> - 커뮤니티가 게시한 훌륭한 **hacking** 기술들을 **STORE**하여 **ORIGINAL** **AUTHORS**에게 모든 **credits**를 제공합니다.
> - **우리는 다른 사람들의 공로를 차지하려는 것이 아닙니다**, 우리는 단지 모두를 위해 멋진 트릭을 저장하고 싶습니다.
> - 우리는 또한 HackTricks에 **자체 연구**를 작성합니다.
> - 몇몇 경우에는 HackTricks에 기술의 중요한 부분을 **요약**만 작성하고, 더 자세한 내용을 위해 **독자에게 원본 게시물을 방문하도록 권장**합니다.
> - 책에 있는 모든 **hacking**술을 **ORGANIZE**하여 더 **MORE ACCESSIBLE**하게 만듭니다.
> - HackTricks 팀은 사람들이 **더 빨리 배울 수 있도록** 콘텐츠 **정리만을 위해** 수천 시간을 무료로 바쳤습니다.
> - 이 책의 목적은 포괄적인 **교육 자료**로서 기능하는 것입니다.
> - 멋진 **hacking** 기법을 **저장**하여 커뮤니티가 게시한 내용에 대해 **원저자들**에게 모든 **크레딧**을 돌립니다.
> - **우리는 다른 사람들의 공로를 원하지 않습니다**, 우리는 단지 모두를 위해 멋진 트릭을 저장하고 싶습니다.
> - 우리는 또한 HackTricks에 **자체 연구**를 게재합니다.
> - 몇몇 경우에는 기술의 중요한 부분에 대한 **HackTricks에 요약**만 작성하고 자세한 내용은 **독자에게 원문 게시물을 방문하도록 권장**할 것입니다.
> - 책에 있는 모든 **hacking**법을 **구성**하여 **더 접근하기 쉽게** 만듭니다.
> - HackTricks 팀은 사람들이 **더 빨리 학습할 수 있도록** 콘텐츠를 **정리하는 데만 수천 시간**을 무료로 투입했습니다.
<figure><img src="../images/hack tricks gif.gif" alt="" width="375"><figcaption></figcaption></figure>
@ -23,35 +23,35 @@
> [!TIP]
>
> - **Thank you so much for these resources, how can I thank you?**
> - **이러한 리소스를 정말 감사드립니다. 어떻게 감사할 수 있나요?**
이 모든 리소스를 공개적으로 정리해 준 HackTricks 팀에게 공개적으로 감사를 전하려면 [**@hacktricks_live**](https://twitter.com/hacktricks_live)를 멘션한 트윗을 올세요.\
특히 감사하다면 [**sponsor the project here**](https://github.com/sponsors/carlospolop)에서 프로젝트를 후원하실 수도 있습니다.\
그리고 Github 프로젝트에 별(star)을 주세요! (아래 링크 참조)
공개적으로 HackTricks 팀이 이 모든 리소스를 모아준 것에 대해 감사하려면 [**@hacktricks_live**](https://twitter.com/hacktricks_live)를 멘션한 트윗을 올려주세요.\
특히 감사하다면 [**여기에서 프로젝트를 후원하세요**](https://github.com/sponsors/carlospolop).\
그리고 Github 프로젝트에 **별표(Star)**를 주는 것을 잊지 마세요! (아래에서 링크를 찾으세요).
> [!TIP]
>
> - **How can I contribute to the project?**
> - **프로젝트에 어떻게 기여할 수 있나요?**
책에서 찾은 버그를 고치거나 **새로운 팁과 트릭을 커뮤니티와 공유**하려면 해당 Github 페이지로 **Pull Request**를 보내세요:
책에서 찾은 버그를 수정하거나 커뮤니티와 새로운 팁과 트릭을 **공유**하려면 해당 Github 페이지에 **Pull Request**를 보내세요:
- [https://github.com/carlospolop/hacktricks](https://github.com/carlospolop/hacktricks)
- [https://github.com/carlospolop/hacktricks-cloud](https://github.com/carlospolop/hacktricks-cloud)
Github 프로젝트에 별(star)을 주는 것도 잊지 마세요!
Github 프로젝트에 **별표(Star)**를 주는 것을 잊지 마세요!
> [!TIP]
>
> - **Can I copy some content from HackTricks and put it in my blog?**
> - **HackTricks의 일부 콘텐츠를 복사하여 내 블로그에 올려도 되나요?**
예, 가능합니다. 다만 컨텐츠를 가져온 특정 링크를 반드시 **명시**하세요.
네, 가능합니다. 단, 콘텐츠를 가져온 **구체적인 링크들을 반드시 언급**하세요.
> [!TIP]
>
> - **How can I cite a page of HackTricks?**
> - **HackTricks의 페이지를 어떻게 인용하나요?**
정보를 가져온 페이지의 링크가 표시되어 있으면 충분합니다.\
bibtex가 필요하면 다음과 같은 형식을 사용할 수 있습니다:
정보를 가져온 페이지의 링크가 표시되는 한 그걸로 충분합니다.\
bibtex가 필요하면 다음과 같은 형식을 사용할 수 있습니다:
```latex
@misc{hacktricks-bibtexing,
author = {"HackTricks Team" or the Authors name of the specific page/trick},
@ -62,82 +62,82 @@ url = {\url{https://book.hacktricks.wiki/specific-page}},
```
> [!WARNING]
>
> - **내 블로그에 HackTricks 전를 복사해도 되나요?**
> - **내 블로그에 HackTricks 전를 복사해도 되나요?**
**그렇게 하지 않는 편이 좋습니다**. 그것은 **아무에게도 도움이 되지 않습니다**. 모든 **콘텐츠는 이미 공식 HackTricks 책에서 무료로 공개되어 있습니다**.
**권장하지 않습니다**. 이는 **아무에게도 이득이 되지 않습니다**. 모든 **콘텐츠는 공식 HackTricks 책에서 이미 무료로 공개되어 있습니다**.
사라질까 걱정된다면, Github에서 포크하거나 다운로드하세요. 앞서 말했듯 이미 무료입니다.
사라질까 걱정된다면 Github에 fork하거나 다운로드하세요. 이미 무료로 제공되고 있습니다.
> [!WARNING]
>
> - **왜 스폰서가 있나요? HackTricks 책은 상업적 목적용인가요?**
> - **왜 스폰서가 있나요? HackTricks 책은 상업적 목적입니까?**
첫 번째 **HackTricks** **가치**는 전 세계 **모두에게 무료(FREE)** 해킹 교육 자료를 제공하는 것입니다. HackTricks 팀은 이 콘텐츠를 제공하기 위해 **수천 시간**을 할애했으며, 다시 말하지만 **무료**로 제공합니다.
첫 번째 **HackTricks** **가치**는 전 세계 **모두**에게 **무료(FREE)** 해킹 교육 자료를 제공하는 것입니다. HackTricks 팀은 이 콘텐츠를 제공하기 위해 **수천 시간**을 바쳤고, 다시 말하지만 **무료**입니다.
HackTricks 책이 **상업적 목적**을 위해 만들어졌다고 생각하신다면 **완전히 잘못된 생각입니다(COMPLETELY WRONG)**.
HackTricks 책이 **상업적 목적**을 위해 만들어졌다고 생각한다면 **완전히 틀렸습니다**.
콘텐츠는 무료지만, 커뮤니티가 우리 작업을 **감사 표시**할 수 있는 선택지를 제공하고 싶기 때문에 스폰서가 있습니다. 따라서 사람들에게 HackTricks에 기부할 수 있는 옵션([**Github sponsors**](https://github.com/sponsors/carlospolop))을 제공하고, **관련 사이버보안 기업들**이 HackTricks를 후원하고 책에 **광고(ads)** 를 게재할 수 있도록 합니다. 광고는 항상 **눈에 띄지만** 학습을 **방해하지 않는** 위치에 배치됩니다.
콘텐츠가 모두 무료이더라도, 커뮤니티가 원하면 우리의 작업을 **감사히 여길 수 있는 선택지**를 제공하고 싶기 때문에 스폰서가 있습니다. 따라서 사람들에게 [**Github sponsors**](https://github.com/sponsors/carlospolop)를 통한 기부 옵션을 제공하고, **관련 사이버보안 회사들**이 HackTricks를 스폰서하고 책에 **광고(ads)**를 게재할 수 있도록 합니다. 광고는 **보이긴 하되 학습을 방해하지 않도록** 항상 콘텐츠를 집중해서 볼 때 방해가 되지 않는 위치에 배치됩니다.
HackTricks는 다른 많은 콘텐츠보다 적은 블로그들처럼 성가신 광고로 가득하지 않습니다. HackTricks는 상업적 목적을 위해 만들어지지 않았습니다.
HackTricks는 콘텐츠가 훨씬 적은 다른 블로그들처럼 성가신 광고로 가득 차 있지 않습니다. HackTricks는 상업적 목적을 위해 만들어진 것이 아닙니다.
> [!CAUTION]
>
> - **HackTricks의 어떤 페이지가 제 블로그 게시물을 기반으로 했는데 출처가 표시되어 있지 않면 어떻게 해야 하나요?**
> - **어떤 HackTricks 페이지가 제 블로그 게시물을 기반으로 했는데 출처가 표시되어 있지 않면 어떻게 해야 하나요?**
**정말 죄송합니다. 이런 일이 발생해서는 안 됩니다**. Github issues, Twitter, Discord... 를 통해 해당 HackTricks 페이지 링크와 귀하의 블로그 링크를 알려주시면 **확인하고 가능한 한 빨리 출처를 추가하겠습니다**.
**정말 죄송합니다. 이런 일이 발생해서는 안 됩니다**. Github issues, Twitter, Discord 등을 통해 HackTricks 페이지 링크와 귀하의 블로그 링크를 알려주시면 **확인 후 가능한 한 빨리 추가하겠습니다**.
> [!CAUTION]
>
> - **제 블로그의 콘텐츠가 HackTricks에 있는데 이를 원하지 않습니다. 어떻게 해야 하나요?**
> - **HackTricks에 제 블로그의 콘텐츠가 있는데 그것을 원하지 않으면 어떻게 해야 하나요?**
HackTricks에 귀하의 페이지 링크가 포함되는 것은 다음과 같은 이점이 있습니다:
HackTricks에 귀하의 페이지로의 링크가 있는 경우 다음과 같은 효과가 있습니다:
- 귀하의 **SEO** 향상
- 콘텐츠가 **15개 이상 언어로 번역**되어 더 많은 사람이 접근 가능
- **HackTricks는** 사람들이 귀하의 페이지를 **확인하도록 권장**합니다(일부 페이지 소유자들은 HackTricks에 자신들의 페이지가 포함된 이후 방문자가 늘었다고 알려왔습니다)
- 귀하의 **SEO**가 향상됩니다
- 해당 콘텐츠는 **15개 이상의 언어로 번역**되어 더 많은 사람들이 접근할 수 있게 됩니다
- **HackTricks는** 사람들에게 귀하의 페이지를 **확인하도록 권장**합니다 (몇몇 분들은 자신들의 페이지가 HackTricks에 게재된 이후 방문자가 늘었다고 알려주셨습니다)
그럼에도 불구하고 귀하의 블로그 콘텐츠가 HackTricks에서 제거되기를 원하시면 알려주십시오. 우리는 귀하의 블로그에 대한 모든 링크와 그 기반의 모든 콘텐츠를 **확실히 제거**하겠습니다.
그럼에도 불구하고 HackTricks에서 귀하의 블로그 콘텐츠를 삭제하길 원하시면 알려주십시오. 저희는 확실히 **귀하의 블로그로의 모든 링크를 제거**하고, 그에 기반한 모든 콘텐츠를 삭제하겠습니다.
> [!CAUTION]
>
> - **HackTricks에서 복사-붙여넣기된 콘텐츠를 발견하면 어떻게 해야 하나요?**
> - **HackTricks에서 복사·붙여넣기된 콘텐츠를 발견하면 어떻게 하나요?**
우리는 항상 **원저자에게 모든 크레딧을 제공합니다**. 만약 출처 표기가 없는 복사-붙여넣기된 페이지를 발견하시면 알려주십시오. 우리는 해당 페이지를 **제거**, **텍스트 앞에 출처 링크 추가**, 또는 **출처 링크를 포함하여 재작성**할 것입니다.
우리는 항상 **원저자에게 모든 크레딧을 부여**합니다. 만약 출처가 표기되지 않은 복사·붙여넣기된 페이지를 발견하신다면 알려주십시오. 저희는 해당 페이지를 **삭제하거나**, **텍스트 앞에 출처 링크를 추가**하거나, **링크를 추가하여 재작성**하겠습니다.
## 라이선스
저작권 © 별도 명시가 없는 한 모든 권리 보유.
Copyright © All rights reserved unless otherwise specified.
#### 라이선스 요약:
- 저작자 표시(Attribution): 다음을 자유롭게 할 수 있습니다:
- Share — 자료를 어떤 매체나 형식으로든 복사하고 재배포할 수 있습니다.
- Adapt — 자료를 리믹스, 변형 및 기반으로 삼아 확장할 수 있습니다.
- Attribution: You are free to:
- Share — copy and redistribute the material in any medium or format.
- Adapt — remix, transform, and build upon the material.
#### 추가 조건:
- 제3자 콘텐츠(Third-Party Content): 이 블로그/책의 일부는 다른 블로그나 출판물의 발췌와 같은 타 출처의 콘텐츠를 포함할 수 있습니다. 이러한 콘텐츠의 사용은 공정 사용(fair use)의 원칙에 따라 이루어지거나 해당 저작권 소유자의 명시적 허가를 받은 것입니다. 제3자 콘텐츠에 대한 특정 라이선스 정보는 원본 출처를 참조하십시오.
- 저작권(Autorship): HackTricks가 작성한 원저작 콘텐츠는 본 라이선스의 조건을 따릅니다. 공유하거나 변형할 때 저자를 표시하도록 권장합니다.
- Third-Party Content: 이 블로그/책의 일부는 다른 출처(예: 다른 블로그나 출판물의 발췌문)를 포함할 수 있습니다. 이러한 콘텐츠의 사용은 공정 사용(fair use)의 원칙에 따라 이루어지거나 해당 저작권자의 명시적 허가를 받아 수행됩니다. 제3자 콘텐츠에 대한 구체적인 라이선스 정보는 원본 출처를 참조하시기 바랍니다.
- Authorship: HackTricks가 저술한 원본 콘텐츠는 이 라이선스의 조건에 따릅니다. 이 작업을 공유하거나 각색할 때 저자를 표시하는 것을 권장합니다.
#### 면제사항:
#### 예외사항:
- 상업적 이용(Commercial Use): 본 콘텐츠의 상업적 이용에 관한 문의는 저에게 연락해 주십시오.
- Commercial Use: 이 콘텐츠의 상업적 이용에 관한 문의는 저에게 연락해 주십시오.
이 라이선스는 콘텐츠와 관련된 상표나 브랜딩 권리를 부여하지 않습니다. 이 블로그/책에 포함된 모든 상표와 브랜딩은 각 소유자의 자산입니다.
이 라이선스는 콘텐츠와 관련된 상표 또는 브랜딩 권리를 부여하지 않습니다. 이 블로그/책에 게재된 모든 상표 및 브랜딩은 해당 소유자의 자산입니다.
**HackTricks에 접근하거나 HackTricks를 사용하는 경우, 귀하는 본 라이선스의 조건을 준수하는 데 동의하는 것입니다. 이 조건에 동의하지 않으면 웹사이트에 접근하지 마십시오.**
**HackTricks에 접근하거나 HackTricks를 사용하는 경우 본 라이선스 조건을 준수하는 데 동의하는 것으로 간주됩니다. 이 조건에 동의하지 않으면 이 웹사이트에 접근하지 마십시오.**
## **면책항**
## **면책항**
> [!CAUTION]
> 이 책 'HackTricks'는 교육적이고 정보 제공 목적을 위해 작성되었습니다. 이 책의 내용은 '있는 그대로(as is)' 제공되며, 저자와 발행인은 정보, 제품, 서비스 또는 관련 그래픽의 완전성, 정확성, 신뢰성, 적합성 또는 가용성에 대해 명시적 또는 묵시적으로 어떠한 진술이나 보증도 하지 않습니다. 따라서 해당 정보에 대한 의존은 전적으로 귀하의 책임입니다.
> 이 책 'HackTricks'는 교육적 및 정보 제공 목적을 위한 것입니다. 이 책 내의 내용은 '있는 그대로(as is)' 제공되며, 저자 및 발행인은 이 책에 포함된 정보, 제품, 서비스 또는 관련 그래픽의 완전성, 정확성, 신뢰성, 적합성 또는 가용성에 대해 명시적이든 묵시적이든 어떠한 진술이나 보증도 하지 않습니다. 따라서 해당 정보에 대한 의존은 전적으로 귀하의 책임입니다.
>
> 저자와 발행인은 어떠한 경우에도 데이터 손실이나 이익 손실 등 간접적 또는 결과적 손해를 포함한 손해에 대해 책임을 지지 않습니다.
> 저자 및 발행인은 데이터 손실 또는 이 책의 사용과 관련하여 발생하는 이익 손실 등 간접적 또는 결과적 손해를 포함하여 어떠한 손실이나 손해에 대해서도 책임을 지지 않습니다.
>
> 또한 본 책에 설명된 기술과 팁은 교육 및 정보 제공 목적으로만 제공되며, 불법적이거나 악의적인 활동에 사용해서는 안 됩니다. 저자와 발행인은 어떠한 불법적이거나 비윤리적인 활동도 용납하거나 지지하지 않으며, 본 책의 정보를 사용하는 것은 전적으로 사용자의 책임과 재량입니다.
> 또한 이 책에 설명된 기술과 팁은 교육적·정보 제공 목적을 위한 것이며 불법적이거나 악의적인 활동에 사용되어서는 안 됩니다. 저자 및 발행인은 어떠한 불법적이거나 비윤리적인 활동도 묵인하거나 지지하지 않으며, 이 책의 정보를 사용함으로써 발생하는 모든 결과는 사용자의 책임과 재량입니다.
>
> 사용자는 본 책에 포함된 정보를 기반으로 취한 모든 조치에 대해 전적으로 책임을 지며, 기술이나 팁을 구현하려 할 때는 항상 전문가의 조언과 도움을 구해야 합니다.
> 이 책에 포함된 정보를 바탕으로 수행한 조치에 대하여 발생한 모든 행동에 대한 책임은 전적으로 사용자에게 있으며, 기술이나 팁을 구현하려 할 때는 항상 전문가의 조언과 도움을 구해야 합니다.
>
> 본 책을 사용함으로써 사용자는 저자와 발행인을 모든 손해, 손실 또는 피해에 대한 책임과 의무로부터 면책하는 데 동의하는 것입니다.
> 이 책을 사용함으로써 사용자는 저자 및 발행인을 이 책 또는 그 안의 정보 사용으로 인해 발생할 수 있는 모든 손해, 손실 또는 해로부터 면책시키는 데 동의하는 것으로 간주됩니다.
{{#include ../banners/hacktricks-training.md}}

View File

@ -6,34 +6,63 @@
*/
(() => {
"use strict";
"use strict";
/* ───────────── 0. helpers (main thread) ───────────── */
const clear = el => { while (el.firstChild) el.removeChild(el.firstChild); };
/* ───────────── 1. WebWorker code ─────────────────── */
const workerCode = `
self.window = self;
self.search = self.search || {};
const abs = p => location.origin + p;
/* 1 — elasticlunr */
try { importScripts('https://cdn.jsdelivr.net/npm/elasticlunr@0.9.5/elasticlunr.min.js'); }
catch { importScripts(abs('/elasticlunr.min.js')); }
/* 2 — decompress gzip data */
async function decompressGzip(arrayBuffer){
if(typeof DecompressionStream !== 'undefined'){
/* Modern browsers: use native DecompressionStream */
const stream = new Response(arrayBuffer).body.pipeThrough(new DecompressionStream('gzip'));
const decompressed = await new Response(stream).arrayBuffer();
return new TextDecoder().decode(decompressed);
} else {
/* Fallback: use pako library */
if(typeof pako === 'undefined'){
try { importScripts('https://cdn.jsdelivr.net/npm/pako@2.1.0/dist/pako.min.js'); }
catch(e){ throw new Error('pako library required for decompression: '+e); }
}
const uint8Array = new Uint8Array(arrayBuffer);
const decompressed = pako.ungzip(uint8Array, {to: 'string'});
return decompressed;
}
}
/* ───────────── 0. helpers (main thread) ───────────── */
const clear = el => { while (el.firstChild) el.removeChild(el.firstChild); };
/* ───────────── 1. WebWorker code ─────────────────── */
const workerCode = `
self.window = self;
self.search = self.search || {};
const abs = p => location.origin + p;
/* 1 — elasticlunr */
try { importScripts('https://cdn.jsdelivr.net/npm/elasticlunr@0.9.5/elasticlunr.min.js'); }
catch { importScripts(abs('/elasticlunr.min.js')); }
/* 2 — load a single index (remote → local) */
/* 3 — load a single index (remote → local) */
async function loadIndex(remote, local, isCloud=false){
let rawLoaded = false;
if(remote){
/* Try ONLY compressed version from GitHub (remote already includes .js.gz) */
try {
const r = await fetch(remote,{mode:'cors'});
if (!r.ok) throw new Error('HTTP '+r.status);
importScripts(URL.createObjectURL(new Blob([await r.text()],{type:'application/javascript'})));
rawLoaded = true;
} catch(e){ console.warn('remote',remote,'failed →',e); }
if (r.ok) {
const compressed = await r.arrayBuffer();
const text = await decompressGzip(compressed);
importScripts(URL.createObjectURL(new Blob([text],{type:'application/javascript'})));
rawLoaded = true;
console.log('Loaded compressed from GitHub:',remote);
}
} catch(e){ console.warn('compressed GitHub',remote,'failed →',e); }
}
/* If remote (GitHub) failed, fall back to local uncompressed file */
if(!rawLoaded && local){
try { importScripts(abs(local)); rawLoaded = true; }
try {
importScripts(abs(local));
rawLoaded = true;
console.log('Loaded local fallback:',local);
}
catch(e){ console.error('local',local,'failed →',e); }
}
if(!rawLoaded) return null; /* give up on this index */
@ -61,151 +90,159 @@
return local ? loadIndex(null, local, isCloud) : null;
}
let built = [];
const MAX = 30, opts = {bool:'AND', expand:true};
self.onmessage = async ({data}) => {
if(data.type === 'init'){
const lang = data.lang || 'en';
const searchindexBase = 'https://raw.githubusercontent.com/HackTricks-wiki/hacktricks-searchindex/master';
(async () => {
const htmlLang = (document.documentElement.lang || 'en').toLowerCase();
const lang = htmlLang.split('-')[0];
const mainReleaseBase = 'https://github.com/HackTricks-wiki/hacktricks/releases/download';
const cloudReleaseBase = 'https://github.com/HackTricks-wiki/hacktricks-cloud/releases/download';
/* Remote sources are .js.gz (compressed), local fallback is .js (uncompressed) */
const mainFilenames = Array.from(new Set(['searchindex-' + lang + '.js.gz', 'searchindex-en.js.gz']));
const cloudFilenames = Array.from(new Set(['searchindex-cloud-' + lang + '.js.gz', 'searchindex-cloud-en.js.gz']));
const mainTags = Array.from(new Set([\`searchindex-\${lang}\`, 'searchindex-en', 'searchindex-master']));
const cloudTags = Array.from(new Set([\`searchindex-\${lang}\`, 'searchindex-en', 'searchindex-master']));
const MAIN_REMOTE_SOURCES = mainFilenames.map(function(filename) { return searchindexBase + '/' + filename; });
const CLOUD_REMOTE_SOURCES = cloudFilenames.map(function(filename) { return searchindexBase + '/' + filename; });
const MAIN_REMOTE_SOURCES = mainTags.map(tag => \`\${mainReleaseBase}/\${tag}/searchindex.js\`);
const CLOUD_REMOTE_SOURCES = cloudTags.map(tag => \`\${cloudReleaseBase}/\${tag}/searchindex.js\`);
const indices = [];
const main = await loadWithFallback(MAIN_REMOTE_SOURCES , '/searchindex.js', false); if(main) indices.push(main);
const cloud= await loadWithFallback(CLOUD_REMOTE_SOURCES, '/searchindex-cloud.js', true ); if(cloud) indices.push(cloud);
if(!indices.length){ postMessage({ready:false, error:'no-index'}); return; }
/* build index objects */
const built = indices.map(d => ({
idx : elasticlunr.Index.load(d.json),
urls: d.urls,
cloud: d.cloud,
base: d.cloud ? 'https://cloud.hacktricks.wiki/' : ''
}));
postMessage({ready:true});
const MAX = 30, opts = {bool:'AND', expand:true};
self.onmessage = ({data:q}) => {
if(!q){ postMessage([]); return; }
const all = [];
for(const s of built){
const res = s.idx.search(q,opts);
if(!res.length) continue;
const max = res[0].score || 1;
res.forEach(r => {
const doc = s.idx.documentStore.getDoc(r.ref);
all.push({
norm : r.score / max,
title: doc.title,
body : doc.body,
breadcrumbs: doc.breadcrumbs,
url : s.base + s.urls[r.ref],
cloud: s.cloud
const indices = [];
const main = await loadWithFallback(MAIN_REMOTE_SOURCES , '/searchindex-book.js', false); if(main) indices.push(main);
const cloud= await loadWithFallback(CLOUD_REMOTE_SOURCES, '/searchindex.js', true ); if(cloud) indices.push(cloud);
if(!indices.length){ postMessage({ready:false, error:'no-index'}); return; }
/* build index objects */
built = indices.map(d => ({
idx : elasticlunr.Index.load(d.json),
urls: d.urls,
cloud: d.cloud,
base: d.cloud ? 'https://cloud.hacktricks.wiki/' : ''
}));
postMessage({ready:true});
return;
}
const q = data.query || data;
if(!q){ postMessage([]); return; }
const all = [];
for(const s of built){
const res = s.idx.search(q,opts);
if(!res.length) continue;
const max = res[0].score || 1;
res.forEach(r => {
const doc = s.idx.documentStore.getDoc(r.ref);
all.push({
norm : r.score / max,
title: doc.title,
body : doc.body,
breadcrumbs: doc.breadcrumbs,
url : s.base + s.urls[r.ref],
cloud: s.cloud
});
});
});
}
all.sort((a,b)=>b.norm-a.norm);
postMessage(all.slice(0,MAX));
};
})();
`;
}
all.sort((a,b)=>b.norm-a.norm);
postMessage(all.slice(0,MAX));
};
`;
/* ───────────── 2. spawn worker ───────────── */
const worker = new Worker(URL.createObjectURL(new Blob([workerCode],{type:'application/javascript'})));
/* ───────────── 2.1. initialize worker with language ───────────── */
const htmlLang = (document.documentElement.lang || 'en').toLowerCase();
const lang = htmlLang.split('-')[0];
worker.postMessage({type: 'init', lang: lang});
/* ───────────── 3. DOM refs ─────────────── */
const wrap = document.getElementById('search-wrapper');
const bar = document.getElementById('searchbar');
const list = document.getElementById('searchresults');
const listOut = document.getElementById('searchresults-outer');
const header = document.getElementById('searchresults-header');
const icon = document.getElementById('search-toggle');
const READY_ICON = icon.innerHTML;
icon.textContent = '⏳';
icon.setAttribute('aria-label','Loading search …');
icon.setAttribute('title','Search is loading, please wait...');
/* ───────────── 2. spawn worker ───────────── */
const worker = new Worker(URL.createObjectURL(new Blob([workerCode],{type:'application/javascript'})));
/* ───────────── 3. DOM refs ─────────────── */
const wrap = document.getElementById('search-wrapper');
const bar = document.getElementById('searchbar');
const list = document.getElementById('searchresults');
const listOut = document.getElementById('searchresults-outer');
const header = document.getElementById('searchresults-header');
const icon = document.getElementById('search-toggle');
const READY_ICON = icon.innerHTML;
icon.textContent = '⏳';
icon.setAttribute('aria-label','Loading search …');
icon.setAttribute('title','Search is loading, please wait...');
const HOT=83, ESC=27, DOWN=40, UP=38, ENTER=13;
let debounce, teaserCount=0;
/* ───────────── helpers (teaser, metric) ───────────── */
const escapeHTML = (()=>{const M={'&':'&amp;','<':'&lt;','>':'&gt;','"':'&#34;','\'':'&#39;'};return s=>s.replace(/[&<>'"]/g,c=>M[c]);})();
const URL_MARK='highlight';
function metric(c,t){return c?`${c} search result${c>1?'s':''} for '${t}':`:`No search results for '${t}'.`;}
function makeTeaser(body,terms){
const stem=w=>elasticlunr.stemmer(w.toLowerCase());
const T=terms.map(stem),W_S=40,W_F=8,W_N=2,WIN=30;
const W=[],sents=body.toLowerCase().split('. ');
let i=0,v=W_F,found=false;
sents.forEach(s=>{v=W_F; s.split(' ').forEach(w=>{ if(w){ if(T.some(t=>stem(w).startsWith(t))){v=W_S;found=true;} W.push([w,v,i]); v=W_N;} i+=w.length+1; }); i++;});
if(!W.length) return body;
const win=Math.min(W.length,WIN);
const sums=[W.slice(0,win).reduce((a,[,wt])=>a+wt,0)];
for(let k=1;k<=W.length-win;k++) sums[k]=sums[k-1]-W[k-1][1]+W[k+win-1][1];
const best=found?sums.lastIndexOf(Math.max(...sums)):0;
const out=[]; i=W[best][2];
for(let k=best;k<best+win;k++){const [w,wt,pos]=W[k]; if(i<pos){out.push(body.substring(i,pos)); i=pos;} if(wt===W_S) out.push('<em>'); out.push(body.substr(pos,w.length)); if(wt===W_S) out.push('</em>'); i=pos+w.length;}
return out.join('');
}
function format(d,terms){
const teaser=makeTeaser(escapeHTML(d.body),terms);
teaserCount++;
const enc=encodeURIComponent(terms.join(' ')).replace(/'/g,'%27');
const parts=d.url.split('#'); if(parts.length===1) parts.push('');
const abs=d.url.startsWith('http');
const href=`${abs?'':path_to_root}${parts[0]}?${URL_MARK}=${enc}#${parts[1]}`;
const style=d.cloud?" style=\"color:#1e88e5\"":"";
const isCloud=d.cloud?" [Cloud]":" [Book]";
return `<a href="${href}" aria-details="teaser_${teaserCount}"${style}>`+
`${d.breadcrumbs}${isCloud}<span class="teaser" id="teaser_${teaserCount}" aria-label="Search Result Teaser">${teaser}</span></a>`;
}
/* ───────────── UI control ───────────── */
function showUI(s){wrap.classList.toggle('hidden',!s); icon.setAttribute('aria-expanded',s); if(s){window.scrollTo(0,0); bar.focus(); bar.select();} else {listOut.classList.add('hidden'); [...list.children].forEach(li=>li.classList.remove('focus'));}}
function blur(){const t=document.createElement('input'); t.style.cssText='position:absolute;opacity:0;'; icon.appendChild(t); t.focus(); t.remove();}
icon.addEventListener('click',()=>showUI(wrap.classList.contains('hidden')));
document.addEventListener('keydown',e=>{
if(e.altKey||e.ctrlKey||e.metaKey||e.shiftKey) return;
const f=/^(?:input|select|textarea)$/i.test(e.target.nodeName);
if(e.keyCode===HOT && !f){e.preventDefault(); showUI(true);} else if(e.keyCode===ESC){e.preventDefault(); showUI(false); blur();}
else if(e.keyCode===DOWN && document.activeElement===bar){e.preventDefault(); const first=list.firstElementChild; if(first){blur(); first.classList.add('focus');}}
else if([DOWN,UP,ENTER].includes(e.keyCode) && document.activeElement!==bar){const cur=list.querySelector('li.focus'); if(!cur) return; e.preventDefault(); if(e.keyCode===DOWN){const nxt=cur.nextElementSibling; if(nxt){cur.classList.remove('focus'); nxt.classList.add('focus');}} else if(e.keyCode===UP){const prv=cur.previousElementSibling; cur.classList.remove('focus'); if(prv){prv.classList.add('focus');} else {bar.focus();}} else {const a=cur.querySelector('a'); if(a) window.location.assign(a.href);}}
});
bar.addEventListener('input',e=>{ clearTimeout(debounce); debounce=setTimeout(()=>worker.postMessage(e.target.value.trim()),120); });
/* ───────────── worker messages ───────────── */
worker.onmessage = ({data}) => {
if(data && data.ready!==undefined){
if(data.ready){
icon.innerHTML=READY_ICON;
icon.setAttribute('aria-label','Open search (S)');
icon.removeAttribute('title');
}
else {
icon.textContent='❌';
icon.setAttribute('aria-label','Search unavailable');
icon.setAttribute('title','Search is unavailable');
}
return;
const HOT=83, ESC=27, DOWN=40, UP=38, ENTER=13;
let debounce, teaserCount=0;
/* ───────────── helpers (teaser, metric) ───────────── */
const escapeHTML = (()=>{const M={'&':'&amp;','<':'&lt;','>':'&gt;','"':'&#34;','\'':'&#39;'};return s=>s.replace(/[&<>'"]/g,c=>M[c]);})();
const URL_MARK='highlight';
function metric(c,t){return c?`${c} search result${c>1?'s':''} for '${t}':`:`No search results for '${t}'.`;}
function makeTeaser(body,terms){
const stem=w=>elasticlunr.stemmer(w.toLowerCase());
const T=terms.map(stem),W_S=40,W_F=8,W_N=2,WIN=30;
const W=[],sents=body.toLowerCase().split('. ');
let i=0,v=W_F,found=false;
sents.forEach(s=>{v=W_F; s.split(' ').forEach(w=>{ if(w){ if(T.some(t=>stem(w).startsWith(t))){v=W_S;found=true;} W.push([w,v,i]); v=W_N;} i+=w.length+1; }); i++;});
if(!W.length) return body;
const win=Math.min(W.length,WIN);
const sums=[W.slice(0,win).reduce((a,[,wt])=>a+wt,0)];
for(let k=1;k<=W.length-win;k++) sums[k]=sums[k-1]-W[k-1][1]+W[k+win-1][1];
const best=found?sums.lastIndexOf(Math.max(...sums)):0;
const out=[]; i=W[best][2];
for(let k=best;k<best+win;k++){const [w,wt,pos]=W[k]; if(i<pos){out.push(body.substring(i,pos)); i=pos;} if(wt===W_S) out.push('<em>'); out.push(body.substr(pos,w.length)); if(wt===W_S) out.push('</em>'); i=pos+w.length;}
return out.join('');
}
const docs=data, q=bar.value.trim(), terms=q.split(/\s+/).filter(Boolean);
header.textContent=metric(docs.length,q);
clear(list);
docs.forEach(d=>{const li=document.createElement('li'); li.innerHTML=format(d,terms); list.appendChild(li);});
listOut.classList.toggle('hidden',!docs.length);
};
})();
function format(d,terms){
const teaser=makeTeaser(escapeHTML(d.body),terms);
teaserCount++;
const enc=encodeURIComponent(terms.join(' ')).replace(/'/g,'%27');
const parts=d.url.split('#'); if(parts.length===1) parts.push('');
const abs=d.url.startsWith('http');
const href=`${abs?'':path_to_root}${parts[0]}?${URL_MARK}=${enc}#${parts[1]}`;
const style=d.cloud?" style=\"color:#1e88e5\"":"";
const isCloud=d.cloud?" [Cloud]":" [Book]";
return `<a href="${href}" aria-details="teaser_${teaserCount}"${style}>`+
`${d.breadcrumbs}${isCloud}<span class="teaser" id="teaser_${teaserCount}" aria-label="Search Result Teaser">${teaser}</span></a>`;
}
/* ───────────── UI control ───────────── */
function showUI(s){wrap.classList.toggle('hidden',!s); icon.setAttribute('aria-expanded',s); if(s){window.scrollTo(0,0); bar.focus(); bar.select();} else {listOut.classList.add('hidden'); [...list.children].forEach(li=>li.classList.remove('focus'));}}
function blur(){const t=document.createElement('input'); t.style.cssText='position:absolute;opacity:0;'; icon.appendChild(t); t.focus(); t.remove();}
icon.addEventListener('click',()=>showUI(wrap.classList.contains('hidden')));
document.addEventListener('keydown',e=>{
if(e.altKey||e.ctrlKey||e.metaKey||e.shiftKey) return;
const f=/^(?:input|select|textarea)$/i.test(e.target.nodeName);
if(e.keyCode===HOT && !f){e.preventDefault(); showUI(true);} else if(e.keyCode===ESC){e.preventDefault(); showUI(false); blur();}
else if(e.keyCode===DOWN && document.activeElement===bar){e.preventDefault(); const first=list.firstElementChild; if(first){blur(); first.classList.add('focus');}}
else if([DOWN,UP,ENTER].includes(e.keyCode) && document.activeElement!==bar){const cur=list.querySelector('li.focus'); if(!cur) return; e.preventDefault(); if(e.keyCode===DOWN){const nxt=cur.nextElementSibling; if(nxt){cur.classList.remove('focus'); nxt.classList.add('focus');}} else if(e.keyCode===UP){const prv=cur.previousElementSibling; cur.classList.remove('focus'); if(prv){prv.classList.add('focus');} else {bar.focus();}} else {const a=cur.querySelector('a'); if(a) window.location.assign(a.href);}}
});
bar.addEventListener('input',e=>{ clearTimeout(debounce); debounce=setTimeout(()=>worker.postMessage({query: e.target.value.trim()}),120); });
/* ───────────── worker messages ───────────── */
worker.onmessage = ({data}) => {
if(data && data.ready!==undefined){
if(data.ready){
icon.innerHTML=READY_ICON;
icon.setAttribute('aria-label','Open search (S)');
icon.removeAttribute('title');
}
else {
icon.textContent='❌';
icon.setAttribute('aria-label','Search unavailable');
icon.setAttribute('title','Search is unavailable');
}
return;
}
const docs=data, q=bar.value.trim(), terms=q.split(/\s+/).filter(Boolean);
header.textContent=metric(docs.length,q);
clear(list);
docs.forEach(d=>{const li=document.createElement('li'); li.innerHTML=format(d,terms); list.appendChild(li);});
listOut.classList.toggle('hidden',!docs.length);
};
})();