mirror of
https://github.com/HackTricks-wiki/hacktricks-cloud.git
synced 2026-07-28 14:47:17 -07:00
Fix translator directive corruption and add Docker Compose (#325)
Co-authored-by: Hermes <hermes@users.noreply.github.com>
This commit is contained in:
@@ -66,6 +66,9 @@ jobs:
|
|||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Validate translator directive safeguards
|
||||||
|
run: python scripts/test_translator_directives.py -v
|
||||||
|
|
||||||
- name: Update and download scripts
|
- name: Update and download scripts
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
# Run locally: docker compose up -> http://localhost:3377
|
||||||
|
services:
|
||||||
|
hacktricks-cloud:
|
||||||
|
image: ghcr.io/hacktricks-wiki/hacktricks-cloud/translator-image:latest
|
||||||
|
platform: linux/amd64 # published image is amd64-only
|
||||||
|
container_name: hacktricks_cloud
|
||||||
|
ports:
|
||||||
|
- "3377:3000"
|
||||||
|
volumes:
|
||||||
|
- .:/app
|
||||||
|
working_dir: /app
|
||||||
|
environment:
|
||||||
|
MDBOOK_PREPROCESSOR__HACKTRICKS__ENV: dev
|
||||||
|
command:
|
||||||
|
- bash
|
||||||
|
- -c
|
||||||
|
- >
|
||||||
|
git config --global --add safe.directory /app &&
|
||||||
|
mdbook serve --hostname 0.0.0.0 --port 3000
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
import unittest
|
||||||
|
|
||||||
|
from translator import preserve_mdbook_directives
|
||||||
|
|
||||||
|
|
||||||
|
class PreserveMdbookDirectivesTests(unittest.TestCase):
|
||||||
|
def test_restores_translated_directive_and_attribute_names(self):
|
||||||
|
source = '''{{#tabs }}
|
||||||
|
{{#tab name="API" }}
|
||||||
|
Use the API.
|
||||||
|
{{#endtab }}
|
||||||
|
{{#endtabs }}'''
|
||||||
|
translated = '''{{#oortjies }}
|
||||||
|
{{#oortjie naam="API" }}
|
||||||
|
Gebruik die API.
|
||||||
|
{{#eindoortjie }}
|
||||||
|
{{#eindoortjies }}'''
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
preserve_mdbook_directives(source, translated),
|
||||||
|
'''{{#tabs }}
|
||||||
|
{{#tab name="API" }}
|
||||||
|
Gebruik die API.
|
||||||
|
{{#endtab }}
|
||||||
|
{{#endtabs }}''',
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_restores_attribute_when_directive_name_was_preserved(self):
|
||||||
|
source = '{{#tab name="API" }}\nUse the API.\n{{#endtab }}'
|
||||||
|
translated = '{{#tab naam="API" }}\nGebruik die API.\n{{#endtab }}'
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
preserve_mdbook_directives(source, translated),
|
||||||
|
'{{#tab name="API" }}\nGebruik die API.\n{{#endtab }}',
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_falls_back_to_source_when_directive_count_changes(self):
|
||||||
|
source = '{{#tabs }}\n{{#endtabs }}'
|
||||||
|
translated = '{{#tabs }}\nVertaalde teks'
|
||||||
|
|
||||||
|
self.assertEqual(preserve_mdbook_directives(source, translated), source)
|
||||||
|
|
||||||
|
def test_leaves_plain_translation_unchanged(self):
|
||||||
|
self.assertEqual(
|
||||||
|
preserve_mdbook_directives('Original prose', 'Translated prose'),
|
||||||
|
'Translated prose',
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
||||||
+41
-4
@@ -22,6 +22,12 @@ MAX_TOKENS = 50000 #gpt-4-1106-preview
|
|||||||
DISALLOWED_SPECIAL = "<|endoftext|>"
|
DISALLOWED_SPECIAL = "<|endoftext|>"
|
||||||
REPLACEMENT_TOKEN = "<END_OF_TEXT>"
|
REPLACEMENT_TOKEN = "<END_OF_TEXT>"
|
||||||
|
|
||||||
|
# mdBook directives are executable document structure, not prose. Models can
|
||||||
|
# occasionally translate directive names or attributes (for example
|
||||||
|
# `name` -> `naam`), which makes preprocessors abort a whole language build.
|
||||||
|
MDBOOK_DIRECTIVE_RE = re.compile(r"\{\{#[^{}]*\}\}")
|
||||||
|
MDBOOK_TAB_OPEN_RE = re.compile(r"\{\{#tab\b([^{}]*)\}\}")
|
||||||
|
|
||||||
TOKENIZER_FALLBACKS = [
|
TOKENIZER_FALLBACKS = [
|
||||||
("gpt-5", "o200k_base"),
|
("gpt-5", "o200k_base"),
|
||||||
("gpt-4o", "o200k_base"),
|
("gpt-4o", "o200k_base"),
|
||||||
@@ -124,9 +130,37 @@ def _get_encoding_for_model(model: str):
|
|||||||
print(f"Tokenizer for model {model} not found. Falling back to {FINAL_TOKENIZER_FALLBACK}.")
|
print(f"Tokenizer for model {model} not found. Falling back to {FINAL_TOKENIZER_FALLBACK}.")
|
||||||
return tiktoken.get_encoding(FINAL_TOKENIZER_FALLBACK)
|
return tiktoken.get_encoding(FINAL_TOKENIZER_FALLBACK)
|
||||||
|
|
||||||
def _fix_translated_shortcodes(text: str) -> str:
|
def preserve_mdbook_directives(source: str, translated: str) -> str:
|
||||||
"""Keep mdBook shortcode attribute names valid after translation."""
|
"""Restore mdBook directives exactly as they appeared in the source.
|
||||||
return re.sub(r'(\{\{#tab\s+)(?!name=)[^=\s}]+=', r'\1name=', text)
|
|
||||||
|
Prompt instructions are not an integrity boundary: a translated directive
|
||||||
|
can still look plausible while being invalid to a preprocessor. When the
|
||||||
|
model preserves the number/order of directives, restore each one
|
||||||
|
byte-for-byte. If it adds or removes one, keep the source chunk instead of
|
||||||
|
committing structurally broken Markdown to a language branch.
|
||||||
|
"""
|
||||||
|
source_directives = MDBOOK_DIRECTIVE_RE.findall(source)
|
||||||
|
if not source_directives:
|
||||||
|
return translated
|
||||||
|
|
||||||
|
translated_directives = MDBOOK_DIRECTIVE_RE.findall(translated)
|
||||||
|
if len(source_directives) != len(translated_directives):
|
||||||
|
print(
|
||||||
|
"Directive count changed during translation "
|
||||||
|
f"({len(source_directives)} -> {len(translated_directives)}); "
|
||||||
|
"returning the source chunk unchanged."
|
||||||
|
)
|
||||||
|
return source
|
||||||
|
|
||||||
|
directives = iter(source_directives)
|
||||||
|
restored = MDBOOK_DIRECTIVE_RE.sub(lambda _match: next(directives), translated)
|
||||||
|
|
||||||
|
for match in MDBOOK_TAB_OPEN_RE.finditer(restored):
|
||||||
|
if not re.search(r'\bname\s*=\s*(["\']).*?\1', match.group(1)):
|
||||||
|
print("Invalid mdBook tab directive after translation; returning the source chunk unchanged.")
|
||||||
|
return source
|
||||||
|
|
||||||
|
return restored
|
||||||
|
|
||||||
def reportTokens(prompt, model):
|
def reportTokens(prompt, model):
|
||||||
encoding = _get_encoding_for_model(model)
|
encoding = _get_encoding_for_model(model)
|
||||||
@@ -306,7 +340,10 @@ Also don't add any extra stuff in your response that is not part of the translat
|
|||||||
response_message = response_message.replace("bypassy", "bypasses") # PL translations translates that from time to time
|
response_message = response_message.replace("bypassy", "bypasses") # PL translations translates that from time to time
|
||||||
response_message = response_message.replace("Bypassy", "Bypasses")
|
response_message = response_message.replace("Bypassy", "Bypasses")
|
||||||
response_message = response_message.replace("-privec.md", "-privesc.md") # PL translations translates that from time to time
|
response_message = response_message.replace("-privec.md", "-privesc.md") # PL translations translates that from time to time
|
||||||
response_message = _fix_translated_shortcodes(response_message)
|
|
||||||
|
# Restore source directives after all model-provided text normalization.
|
||||||
|
# This preserves exact mdBook syntax while retaining translated prose.
|
||||||
|
response_message = preserve_mdbook_directives(text, response_message)
|
||||||
|
|
||||||
# Sometimes chatgpt modified the number of "#" at the beginning of the text, so we need to fix that. This is specially important for the first line of the MD that mucst have only 1 "#"
|
# Sometimes chatgpt modified the number of "#" at the beginning of the text, so we need to fix that. This is specially important for the first line of the MD that mucst have only 1 "#"
|
||||||
cont2 = 0
|
cont2 = 0
|
||||||
|
|||||||
+10
-2
@@ -13,7 +13,7 @@ _Hacktricks logos & motion designed by_ [_@ppieranacho_](https://www.instagram.c
|
|||||||
git clone https://github.com/HackTricks-wiki/hacktricks-cloud
|
git clone https://github.com/HackTricks-wiki/hacktricks-cloud
|
||||||
|
|
||||||
# Select the language you want to use
|
# Select the language you want to use
|
||||||
export LANG="master" # Leave master for English
|
export HT_LANG="master" # Leave master for English
|
||||||
# "af" for Afrikaans
|
# "af" for Afrikaans
|
||||||
# "de" for German
|
# "de" for German
|
||||||
# "el" for Greek
|
# "el" for Greek
|
||||||
@@ -32,11 +32,19 @@ export LANG="master" # Leave master for English
|
|||||||
# "zh" for Chinese
|
# "zh" for Chinese
|
||||||
|
|
||||||
# Run the docker container indicating the path to the hacktricks-cloud folder
|
# Run the docker container indicating the path to the hacktricks-cloud folder
|
||||||
docker run -d --rm --platform linux/amd64 -p 3377:3000 --name hacktricks_cloud -v $(pwd)/hacktricks-cloud:/app ghcr.io/hacktricks-wiki/hacktricks-cloud/translator-image bash -c "mkdir -p ~/.ssh && ssh-keyscan -H github.com >> ~/.ssh/known_hosts && cd /app && git checkout $LANG && git pull && MDBOOK_PREPROCESSOR__HACKTRICKS__ENV=dev mdbook serve --hostname 0.0.0.0"
|
docker run -d --rm --platform linux/amd64 -p 3377:3000 --name hacktricks_cloud -v $(pwd)/hacktricks-cloud:/app ghcr.io/hacktricks-wiki/hacktricks-cloud/translator-image bash -c "mkdir -p ~/.ssh && ssh-keyscan -H github.com >> ~/.ssh/known_hosts && cd /app && git checkout $HT_LANG && git pull && MDBOOK_PREPROCESSOR__HACKTRICKS__ENV=dev mdbook serve --hostname 0.0.0.0"
|
||||||
```
|
```
|
||||||
|
|
||||||
Your local copy of HackTricks Cloud will be **available at [http://localhost:3377](http://localhost:3377)** after a minute.
|
Your local copy of HackTricks Cloud will be **available at [http://localhost:3377](http://localhost:3377)** after a minute.
|
||||||
|
|
||||||
|
Alternatively, if you have Docker Compose, run this from the repository root:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose up
|
||||||
|
```
|
||||||
|
|
||||||
|
The bundled `docker-compose.yml` serves your currently checked-out branch at [http://localhost:3377](http://localhost:3377) with live reload.
|
||||||
|
|
||||||
### **Pentesting CI/CD Methodology**
|
### **Pentesting CI/CD Methodology**
|
||||||
|
|
||||||
**In the HackTricks CI/CD Methodology you will find how to pentest infrastructure related to CI/CD activities.** Read the following page for an **introduction:**
|
**In the HackTricks CI/CD Methodology you will find how to pentest infrastructure related to CI/CD activities.** Read the following page for an **introduction:**
|
||||||
|
|||||||
Reference in New Issue
Block a user