wuptracker 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
wuptracker/profiles.py ADDED
@@ -0,0 +1,293 @@
1
+ """Perfis (domínio) e estilos (formato) do writeup.
2
+
3
+ DOMÍNIO — o que a sessão é, define o prompt de visão e o foco do conteúdo:
4
+ "thm" : pentest / CTF (TryHackMe, HTB...)
5
+ "generico" : qualquer sessão de trabalho no computador
6
+
7
+ ESTILO — como o writeup é escrito:
8
+ "tecnico" : denso e preciso, com seções fixas e âncoras de horário (padrão)
9
+ "corrido" : narrativa fluida em texto corrido
10
+ "resumo" : resumo executivo curto
11
+ "blog" : artigo técnico didático (dev.to / Medium)
12
+ "linkedin" : post pronto para publicar no LinkedIn
13
+ """
14
+
15
+ # ===========================================================================
16
+ # DOMÍNIOS
17
+ # ===========================================================================
18
+ THM_ANALYZE = """Você está analisando um screenshot de uma sessão de pentest/CTF no TryHackMe.
19
+
20
+ Analise a imagem e responda SOMENTE em JSON válido, sem markdown, sem texto fora do JSON:
21
+
22
+ {
23
+ "phase": "reconhecimento | enumeracao | acesso_inicial | pos_exploracao | privesc | web | crypto | stego | misc",
24
+ "title": "Título curto descritivo (max 60 chars)",
25
+ "description": "Descrição técnica do que está acontecendo na tela",
26
+ "commands": ["lista de comandos visíveis no terminal, se houver"],
27
+ "findings": ["informações relevantes encontradas: IPs, portas, usuários, hashes, flags"],
28
+ "tool": "ferramenta principal visível (nmap, burp, gobuster, metasploit, etc.)",
29
+ "is_key_moment": true,
30
+ "key_moment_reason": "por que é importante (flag encontrada, shell obtida, vuln confirmada) - null se não for key moment",
31
+ "crop": {"x": 0.0, "y": 0.0, "w": 1.0, "h": 1.0}
32
+ }
33
+
34
+ Em "crop", indique o retângulo que contém APENAS o conteúdo relevante (a janela do
35
+ terminal, o painel do Burp, o diálogo, o trecho de saída importante) — descartando
36
+ barra de tarefas, área de trabalho vazia, outras janelas irrelevantes. Use frações
37
+ de 0 a 1 relativas à largura/altura da imagem: x,y = canto superior esquerdo;
38
+ w,h = largura/altura. Se a tela inteira for relevante, use x:0, y:0, w:1, h:1."""
39
+
40
+ GEN_ANALYZE = """Você está analisando um screenshot de uma sessão de trabalho no computador
41
+ (pode ser desenvolvimento, pesquisa, configuração de sistema, debugging, design,
42
+ administração, estudo — qualquer coisa).
43
+
44
+ Analise a imagem e responda SOMENTE em JSON válido, sem markdown, sem texto fora do JSON:
45
+
46
+ {
47
+ "category": "codigo | terminal | navegador | config | leitura | design | comunicacao | erro | outro",
48
+ "title": "Título curto descritivo do que está acontecendo (max 60 chars)",
49
+ "description": "Descrição objetiva do que está sendo feito ou mostrado na tela",
50
+ "actions": ["passos, comandos, edições ou ações concretas visíveis"],
51
+ "findings": ["descobertas, resultados, valores, mensagens de erro, decisões relevantes"],
52
+ "app": "aplicativo ou ferramenta principal visível (VS Code, terminal, Firefox, etc.)",
53
+ "is_key_moment": true,
54
+ "key_moment_reason": "por que este momento importa (algo funcionou, um bug foi encontrado, uma decisão foi tomada, um marco foi atingido) - null se não for um momento-chave",
55
+ "crop": {"x": 0.0, "y": 0.0, "w": 1.0, "h": 1.0}
56
+ }
57
+
58
+ Em "crop", indique o retângulo que contém APENAS o conteúdo relevante (a janela ou
59
+ painel em foco, o trecho de código, o diálogo, a mensagem de erro) — descartando
60
+ barra de tarefas, área de trabalho vazia e janelas irrelevantes ao fundo. Use
61
+ frações de 0 a 1 relativas à largura/altura da imagem: x,y = canto superior
62
+ esquerdo; w,h = largura/altura. Se a tela inteira for relevante, use x:0, y:0,
63
+ w:1, h:1."""
64
+
65
+ THM_SECTIONS = """# {room_name} — TryHackMe
66
+
67
+ ## Reconhecimento
68
+ (nmap, scans iniciais, portas abertas)
69
+
70
+ ## Enumeração
71
+ (serviços explorados, informações coletadas)
72
+
73
+ ## Acesso Inicial
74
+ (como foi obtido o primeiro acesso)
75
+
76
+ ## Pós-Exploração
77
+ (movimentação, arquivos encontrados, flags de usuário)
78
+
79
+ ## Escalação de Privilégio
80
+ (vetor usado, como foi obtido root)
81
+
82
+ ## Flags
83
+ | Flag | Valor |
84
+ |---|---|
85
+ | User | ... |
86
+ | Root | ... |
87
+
88
+ ## Ferramentas Utilizadas
89
+ (lista com link para cada ferramenta mencionada)
90
+
91
+ ## Lições Aprendidas
92
+ (técnicas principais, conceitos importantes da room)
93
+
94
+ ## Referências
95
+ (CVEs mencionados, links relevantes)"""
96
+
97
+ GEN_SECTIONS = """# {room_name}
98
+
99
+ ## Resumo
100
+ (2-4 frases: qual era o objetivo da sessão e onde ela chegou)
101
+
102
+ ## Linha do Tempo
103
+ (narrativa cronológica do que foi feito, passo a passo, usando os horários
104
+ [HH:MM:SS] das capturas como âncora; agrupe passos relacionados)
105
+
106
+ ## Descobertas e Resultados
107
+ (o que foi aprendido, valores/configurações importantes, o que passou a funcionar)
108
+
109
+ ## Problemas Encontrados
110
+ (erros, becos sem saída, pendências — com a mensagem de erro quando disponível)
111
+
112
+ ## Estado Final e Próximos Passos
113
+ (onde as coisas ficaram ao fim da sessão; o que falta fazer)
114
+
115
+ ## Ferramentas e Referências
116
+ (apps usados; links/documentação que apareceram)"""
117
+
118
+ DOMAINS = {
119
+ "thm": {
120
+ "analyze_prompt": THM_ANALYZE,
121
+ "label": "sessão de pentest/CTF no TryHackMe",
122
+ "focus": ("Sessão de segurança ofensiva. Foque em vetores de ataque, "
123
+ "comandos, vulnerabilidades, credenciais e flags."),
124
+ "sections": THM_SECTIONS,
125
+ "tags": "thm, writeup",
126
+ },
127
+ "generico": {
128
+ "analyze_prompt": GEN_ANALYZE,
129
+ "label": "sessão de trabalho no computador",
130
+ "focus": ("Pode ser desenvolvimento, pesquisa, configuração, debugging "
131
+ "ou estudo. Foque no que foi tentado, no que funcionou, no que "
132
+ "deu errado e no que foi descoberto."),
133
+ "sections": GEN_SECTIONS,
134
+ "tags": "writeup, log",
135
+ },
136
+ }
137
+
138
+ # ===========================================================================
139
+ # ESTILOS
140
+ # ===========================================================================
141
+ STYLES = {
142
+ "tecnico": """Markdown técnico e preciso, começando com frontmatter YAML:
143
+ ---
144
+ tags: [{tags}, {fase_tags}]
145
+ criado: {data_hoje}
146
+ ---
147
+
148
+ Depois, estruture EXATAMENTE com estas seções (omita uma seção se realmente não
149
+ houver conteúdo para ela; não invente):
150
+
151
+ {sections}
152
+
153
+ - Mantenha as âncoras de horário [HH:MM:SS] das capturas ao longo do texto
154
+ - Bloco de código para todo comando, trecho de código e mensagem de erro
155
+ - Denso em informação, direto ao ponto""",
156
+
157
+ "corrido": """Markdown em texto corrido: uma narrativa fluida, impessoal ou em 1ª pessoa
158
+ do plural, em ordem cronológica — o que foi feito, o que se descobriu, como terminou.
159
+ - Frontmatter mínimo (tags: [{tags}, {fase_tags}], criado: {data_hoje})
160
+ - Um título H1 e poucos ou nenhum subtítulo; deixe o texto fluir em parágrafos
161
+ - Sem tabelas e sem listas de "fases"; incorpore os detalhes na prosa
162
+ - Ainda use blocos de código para comandos e trechos relevantes
163
+ - Cite horários pontualmente só quando ajudar""",
164
+
165
+ "resumo": """Resumo executivo curto em Markdown (máx. ~250 palavras):
166
+ - Frontmatter mínimo (tags: [{tags}], criado: {data_hoje})
167
+ - Um título H1
168
+ - 1 parágrafo de contexto (qual era o objetivo)
169
+ - 5 a 10 bullets com o essencial do que foi feito e descoberto
170
+ - 1 parágrafo de conclusão / estado final / próximos passos
171
+ - Sem linha do tempo detalhada, sem tabelas""",
172
+
173
+ "blog": """Artigo de blog técnico em Markdown (estilo dev.to / Medium):
174
+ - Frontmatter (tags: [{tags}, {fase_tags}], criado: {data_hoje})
175
+ - Título H1 chamativo, porém honesto
176
+ - Introdução que situa o leitor (o problema, o desafio)
177
+ - Desenvolvimento em seções com subtítulos temáticos (não por "fase"), em ordem lógica
178
+ - Blocos de código comentados; explique o raciocínio, não só o comando
179
+ - Seção final de conclusão e "o que aprendi"
180
+ - Tom didático: um leitor competente que não acompanhou a sessão""",
181
+
182
+ "linkedin": """Post pronto para publicar no LinkedIn. TEXTO PURO — não use markdown,
183
+ nem frontmatter, nem títulos com #.
184
+ - 1ª pessoa, tom pessoal e acessível, mas tecnicamente correto
185
+ - Primeira linha = gancho forte (o que você fez ou aprendeu)
186
+ - Corpo curto (120–220 palavras) contando a jornada em poucas frases
187
+ - 3 a 5 aprendizados práticos em bullets (use "•" ou "-")
188
+ - Encerre com uma pergunta ou convite à discussão
189
+ - 3 a 6 hashtags relevantes na última linha
190
+ - NÃO exponha dados sensíveis: IPs/hosts internos, flags, credenciais, nomes de
191
+ cliente — generalize ("uma máquina alvo", "um serviço web")""",
192
+ }
193
+
194
+ STYLES_WITH_IMAGES = {"tecnico", "corrido", "blog"}
195
+
196
+ WRITEUP_TEMPLATE = """Você está produzindo um registro ("writeup") de uma {domain_label},
197
+ a partir dos screenshots capturados ao longo da sessão e da análise de cada um.
198
+
199
+ {domain_focus}
200
+
201
+ METADADOS DA SESSÃO:
202
+ - Título/Room: {room_name}
203
+ - Duração: {duration}
204
+ - Total de capturas: {total_captures}
205
+ - Ferramentas/apps: {tools_list}
206
+
207
+ CAPTURAS (em ordem cronológica):
208
+ {captures_context}
209
+
210
+ FORMATO DE SAÍDA — estilo "{style_name}":
211
+ {style_block}
212
+
213
+ REGRAS GERAIS:
214
+ - Baseie-se somente no que aparece nas capturas e análises; se não tiver certeza,
215
+ omita em vez de inventar
216
+ - Não force um enquadramento que não existe (se não é pentest, não escreva como pentest)
217
+ - {image_rule}
218
+ - Responda SOMENTE com o conteúdo final do writeup, sem nenhum comentário seu."""
219
+
220
+ IMAGE_RULE_ON = (
221
+ 'IMAGENS: quando uma captura trouxer a linha "Imagem: CAMINHO", incorpore-a no '
222
+ "ponto do texto onde é discutida, com a sintaxe exata ![titulo curto](CAMINHO) "
223
+ "usando o CAMINHO fornecido sem alterá-lo, cada imagem no máximo uma vez")
224
+ IMAGE_RULE_OFF = 'IMAGENS: ignore as linhas "Imagem:" das capturas — este estilo não usa imagens'
225
+
226
+ # ===========================================================================
227
+ # Resolução de nomes
228
+ # ===========================================================================
229
+ DOMAIN_ALIASES = {
230
+ "generic": "generico", "geral": "generico", "trabalho": "generico",
231
+ "log": "generico", "tryhackme": "thm", "pentest": "thm", "ctf": "thm", "htb": "thm",
232
+ }
233
+ STYLE_ALIASES = {
234
+ "técnico": "tecnico", "technical": "tecnico", "default": "tecnico", "padrao": "tecnico",
235
+ "narrativo": "corrido", "prosa": "corrido", "narrative": "corrido",
236
+ "resumido": "resumo", "tldr": "resumo", "breve": "resumo", "executivo": "resumo",
237
+ "artigo": "blog", "medium": "blog", "devto": "blog",
238
+ "social": "linkedin", "post": "linkedin", "li": "linkedin",
239
+ }
240
+
241
+
242
+ def resolve_domain(name):
243
+ if not name:
244
+ return "thm"
245
+ name = name.strip().lower()
246
+ name = DOMAIN_ALIASES.get(name, name)
247
+ if name not in DOMAINS:
248
+ raise SystemExit(
249
+ f"[!] Perfil inválido: {name!r}. Use: {', '.join(DOMAINS)}")
250
+ return name
251
+
252
+
253
+ def resolve_style(name):
254
+ if not name:
255
+ return "tecnico"
256
+ name = name.strip().lower()
257
+ name = STYLE_ALIASES.get(name, name)
258
+ if name not in STYLES:
259
+ raise SystemExit(
260
+ f"[!] Estilo inválido: {name!r}. Use: {', '.join(STYLES)}")
261
+ return name
262
+
263
+
264
+ # compat com chamadas antigas (capture.py usa o analyze_prompt)
265
+ resolve = resolve_domain
266
+ PROFILES = DOMAINS
267
+
268
+
269
+ def get(name):
270
+ return DOMAINS[resolve_domain(name)]
271
+
272
+
273
+ def build_writeup_prompt(domain, style, *, room_name, duration, total_captures,
274
+ tools_list, captures_context, fase_tags, data_hoje):
275
+ d = DOMAINS[resolve_domain(domain)]
276
+ sname = resolve_style(style)
277
+
278
+ sections = d["sections"].replace("{room_name}", room_name)
279
+ style_block = STYLES[sname].format(
280
+ sections=sections, tags=d["tags"], fase_tags=fase_tags, data_hoje=data_hoje)
281
+
282
+ return WRITEUP_TEMPLATE.format(
283
+ domain_label=d["label"],
284
+ domain_focus=d["focus"],
285
+ room_name=room_name,
286
+ duration=duration,
287
+ total_captures=total_captures,
288
+ tools_list=tools_list,
289
+ captures_context=captures_context,
290
+ style_name=sname,
291
+ style_block=style_block,
292
+ image_rule=IMAGE_RULE_ON if sname in STYLES_WITH_IMAGES else IMAGE_RULE_OFF,
293
+ )