mirror of
https://github.com/anotherhadi/blog.git
synced 2026-05-20 13:32:33 +02:00
Compare commits
6 Commits
eea8c3e9be
...
35ac328d5e
| Author | SHA1 | Date | |
|---|---|---|---|
| 35ac328d5e | |||
| 5ad26be352 | |||
| fac0a2fff6 | |||
| d2424e0a17 | |||
| 3b17b01d86 | |||
| 0e83788a15 |
@@ -356,6 +356,10 @@ const {
|
||||
Projects
|
||||
<ArrowRight class="size-4" />
|
||||
</a>
|
||||
<a href="/notes" class="btn btn-ghost gap-2">
|
||||
Infosec Notes
|
||||
<ArrowRight class="size-4" />
|
||||
</a>
|
||||
<a href="/#contact" class="btn btn-ghost gap-2">
|
||||
Contact Me
|
||||
<ArrowRight class="size-4" />
|
||||
|
||||
@@ -4,7 +4,7 @@ const pathname = Astro.url.pathname;
|
||||
const links = [
|
||||
{ href: "/", label: "home" },
|
||||
{ href: "/blog", label: "blog" },
|
||||
{ href: "/notes", label: "notes" },
|
||||
{ href: "/notes", label: "infosec notes" },
|
||||
{ href: "/projects", label: "projects" },
|
||||
];
|
||||
|
||||
@@ -28,7 +28,11 @@ function isActive(href: string) {
|
||||
~/hadi
|
||||
</a>
|
||||
|
||||
<div id="oneko-track" transition:persist class="flex-1 relative h-12 pointer-events-none">
|
||||
<div
|
||||
id="oneko-track"
|
||||
transition:persist
|
||||
class="flex-1 relative h-12 pointer-events-none"
|
||||
>
|
||||
</div>
|
||||
|
||||
<nav class="hidden md:flex items-center">
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { slide } from "svelte/transition";
|
||||
import { untrack } from "svelte";
|
||||
|
||||
interface Note {
|
||||
id: string;
|
||||
@@ -9,11 +10,12 @@
|
||||
|
||||
interface Props {
|
||||
notes: Note[];
|
||||
currentEntry: Note;
|
||||
currentEntry?: Note;
|
||||
currentCategory?: string;
|
||||
categories: string[];
|
||||
}
|
||||
|
||||
const { notes, currentEntry, categories }: Props = $props();
|
||||
const { notes, currentEntry, currentCategory, categories }: Props = $props();
|
||||
|
||||
let search = $state("");
|
||||
|
||||
@@ -46,8 +48,12 @@
|
||||
: title.includes(term) || tags.join(",").includes(term);
|
||||
}
|
||||
|
||||
const activeCategory = $derived(
|
||||
currentCategory ?? (currentEntry ? getCategory(currentEntry) : null),
|
||||
);
|
||||
|
||||
let openCategories = $state<string[]>(
|
||||
categories.filter((c) => c === getCategory(currentEntry)),
|
||||
untrack(() => categories.filter((c) => c === activeCategory)),
|
||||
);
|
||||
|
||||
function toggle(cat: string) {
|
||||
@@ -99,32 +105,42 @@
|
||||
(n) => getCategory(n) === cat && matchesSearch(n),
|
||||
)}
|
||||
{#if catNotes.length > 0 || !search}
|
||||
{@const isFolder = notes.some((n) => n.id.includes("/") && getCategory(n) === cat)}
|
||||
<div>
|
||||
<!-- Category header -->
|
||||
<button
|
||||
onclick={() => toggle(cat)}
|
||||
class="w-full flex items-center gap-1.5 px-2 py-1 rounded-md hover:bg-base-200/40 transition-colors duration-150 group"
|
||||
>
|
||||
<svg
|
||||
class="w-3 h-3 text-base-content/35 shrink-0 transition-transform duration-200"
|
||||
class:rotate-90={openCategories.includes(cat)}
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
<div class="flex items-center w-full">
|
||||
<button
|
||||
onclick={() => toggle(cat)}
|
||||
class="flex items-center gap-1.5 px-2 py-1 rounded-md hover:bg-base-200/40 transition-colors duration-150 shrink-0"
|
||||
>
|
||||
<path d="m9 18 6-6-6-6" />
|
||||
</svg>
|
||||
<span class="text-primary/50 font-mono text-xs shrink-0">/</span>
|
||||
<span
|
||||
class="font-bold tracking-tight text-sm truncate text-base-content/80"
|
||||
>
|
||||
{cat}
|
||||
</span>
|
||||
</button>
|
||||
<svg
|
||||
class="w-3 h-3 text-base-content/35 shrink-0 transition-transform duration-200"
|
||||
class:rotate-90={openCategories.includes(cat)}
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="m9 18 6-6-6-6" />
|
||||
</svg>
|
||||
<span class="text-primary/50 font-mono text-xs shrink-0">/</span>
|
||||
</button>
|
||||
{#if isFolder}
|
||||
<a
|
||||
href={`/notes/${cat}`}
|
||||
class="flex-1 min-w-0 px-1 py-1 rounded-md hover:bg-base-200/40 transition-colors duration-150 font-bold tracking-tight text-sm truncate text-base-content/80 hover:text-base-content"
|
||||
>
|
||||
{cat}
|
||||
</a>
|
||||
{:else}
|
||||
<span class="flex-1 min-w-0 px-1 py-1 font-bold tracking-tight text-sm truncate text-base-content/80">
|
||||
{cat}
|
||||
</span>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<!-- Notes list -->
|
||||
{#if openCategories.includes(cat)}
|
||||
@@ -133,17 +149,16 @@
|
||||
transition:slide={{ duration: 180 }}
|
||||
>
|
||||
{#each catNotes as note}
|
||||
<li>
|
||||
<li class="tooltip tooltip-right w-full" data-tip={note.data.title}>
|
||||
<a
|
||||
href={`/notes/${note.id}`}
|
||||
title={note.data.title}
|
||||
class="flex items-center gap-1.5 px-2 py-1 rounded-md text-xs font-mono truncate transition-colors duration-150
|
||||
{note.id === currentEntry.id
|
||||
{currentEntry && note.id === currentEntry.id
|
||||
? 'text-primary/90 bg-primary/10'
|
||||
: 'text-base-content/45 hover:text-base-content/80 hover:bg-base-200/40'}"
|
||||
>
|
||||
<span class="shrink-0 font-mono text-base-content/20">
|
||||
{note.id === currentEntry.id ? "▸" : "–"}
|
||||
{currentEntry && note.id === currentEntry.id ? "▸" : "–"}
|
||||
</span>
|
||||
<span class="truncate">{note.data.title}</span>
|
||||
</a>
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
---
|
||||
title: "FTP"
|
||||
description: "Enumeration, exploitation and post-exploitation techniques for FTP servers."
|
||||
tags: ["ftp", "network", "service"]
|
||||
publishDate: 2026-04-29
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
FTP runs on **port 21** (control) and uses a secondary data channel (port 20 for active, ephemeral port for passive).
|
||||
Common implementations: vsftpd, ProFTPD, Pure-FTPd, FileZilla Server, IIS FTP.
|
||||
|
||||
## Enumeration
|
||||
|
||||
### Banner grabbing
|
||||
|
||||
```bash
|
||||
nc -nv $IP 21
|
||||
ftp $IP
|
||||
```
|
||||
|
||||
The banner often reveals the software version: cross-reference with CVE databases.
|
||||
|
||||
### Nmap
|
||||
|
||||
```bash
|
||||
nmap -sV -p 21 $IP
|
||||
nmap -p 21 --script ftp-* $IP
|
||||
```
|
||||
|
||||
Key scripts:
|
||||
|
||||
- `ftp-anon`: checks anonymous login
|
||||
- `ftp-bounce`: tests for FTP bounce attack
|
||||
- `ftp-brute`: brute-force credentials
|
||||
- `ftp-syst`: retrieves system info
|
||||
|
||||
## Anonymous Login
|
||||
|
||||
```bash
|
||||
ftp $IP
|
||||
# Username: anonymous
|
||||
# Password: <empty> or anonymous@
|
||||
```
|
||||
|
||||
If allowed, list and download everything:
|
||||
|
||||
```bash
|
||||
ls -la
|
||||
mget *
|
||||
```
|
||||
|
||||
Check for writable directories: you may be able to upload a webshell if FTP root overlaps with a web root.
|
||||
|
||||
## Brute Force
|
||||
|
||||
```bash
|
||||
hydra -l $user -P /usr/share/wordlists/rockyou.txt ftp://$IP
|
||||
medusa -h $IP -u $user -P /usr/share/wordlists/rockyou.txt -M ftp
|
||||
```
|
||||
|
||||
Try default credentials first: `admin:admin`, `ftp:ftp`, `user:password`.
|
||||
@@ -1,14 +0,0 @@
|
||||
---
|
||||
title: "Notes in comming.."
|
||||
description: ""
|
||||
tags: []
|
||||
publishDate: 2026-04-24
|
||||
---
|
||||
|
||||
## Notes
|
||||
|
||||
Salut comment ça va ! $test1
|
||||
|
||||
```bash
|
||||
nmap -p $port "$Ip"
|
||||
```
|
||||
@@ -0,0 +1,97 @@
|
||||
---
|
||||
title: "Bluesky"
|
||||
description: "Enumeration, search operators, API endpoints and tools for investigating Bluesky accounts."
|
||||
tags: ["osint", "bluesky", "social-media", "enumeration"]
|
||||
publishDate: 2026-04-29
|
||||
---
|
||||
|
||||
## Key Concepts
|
||||
|
||||
Bluesky is built on the **AT Protocol**. Every account has two identifiers:
|
||||
|
||||
- **Handle**: `user.bsky.social` or a custom domain (can change)
|
||||
- **DID**: `did:plc:ewvi7nxzyoun6zhxrhs64oiz` (permanent, survives handle changes)
|
||||
|
||||
All public content is accessible **without an account**. Follower/following lists are also public by default.
|
||||
|
||||
## Account Enumeration
|
||||
|
||||
### Resolve handle → DID
|
||||
|
||||
```
|
||||
https://bsky.social/xrpc/com.atproto.identity.resolveHandle?handle=$HANDLE
|
||||
```
|
||||
|
||||
### Resolve DID → history (all past handles, keys, creation date)
|
||||
|
||||
```
|
||||
https://plc.directory/$DID
|
||||
```
|
||||
|
||||
### Get profile metadata
|
||||
|
||||
```
|
||||
https://public.api.bsky.app/xrpc/app.bsky.actor.getProfile?actor=$HANDLE
|
||||
```
|
||||
|
||||
Returns: DID, display name, description, follower/following count, creation date, avatar URL.
|
||||
|
||||
### Followers / following
|
||||
|
||||
```
|
||||
https://public.api.bsky.app/xrpc/app.bsky.graph.getFollowers?actor=$HANDLE&limit=100
|
||||
https://public.api.bsky.app/xrpc/app.bsky.graph.getFollows?actor=$HANDLE&limit=100
|
||||
```
|
||||
|
||||
Paginate with the `cursor` field from the response.
|
||||
|
||||
## Search Operators
|
||||
|
||||
Bluesky's full-text search supports these operators (combinable):
|
||||
|
||||
| Operator | Example | Effect |
|
||||
| ----------- | ----------------------------- | ----------------------------- |
|
||||
| `"..."` | `"exact phrase"` | Exact match |
|
||||
| `from:` | `from:handle.bsky.social` | Posts by user |
|
||||
| `mentions:` | `mentions:handle.bsky.social` | Posts mentioning user |
|
||||
| `since:` | `since:2024-01-01` | After date (UTC, YYYY-MM-DD) |
|
||||
| `until:` | `until:2024-06-30` | Before date (UTC, YYYY-MM-DD) |
|
||||
| `lang:` | `lang:fr` | Language (ISO 639-1) |
|
||||
| `domain:` | `domain:github.com` | Posts linking to domain |
|
||||
| `#tag` | `#osint` | Hashtag |
|
||||
|
||||
#### API equivalent
|
||||
|
||||
```
|
||||
https://public.api.bsky.app/xrpc/app.bsky.feed.searchPosts?q={QUERY}&author={HANDLE}&since=2024-01-01&until=2024-12-31&lang=en&limit=25
|
||||
```
|
||||
|
||||
## Google Dorks
|
||||
|
||||
Bluesky is heavily indexed by Google. Useful for finding profiles and posts without touching the platform:
|
||||
|
||||
```
|
||||
site:bsky.app "$TARGET_NAME"
|
||||
site:bsky.app "$TARGET_NAME" inurl:profile
|
||||
site:bsky.app "$KEYWORD" since:2024-01-01
|
||||
```
|
||||
|
||||
## Tools
|
||||
|
||||
### BlueSkyNet
|
||||
|
||||
Web app for searching and exporting Bluesky data to CSV. Wraps the public API with a UI for advanced search filters.
|
||||
|
||||
- [github.com/jakecreps/blueskynet](https://github.com/jakecreps/blueskynet)
|
||||
|
||||
### ClearSky
|
||||
|
||||
Shows block lists, blocking history, and who blocked a given account. Useful for mapping relationships and adversarial clusters.
|
||||
|
||||
- [clearsky.app](https://clearsky.app)
|
||||
|
||||
### plc.directory
|
||||
|
||||
Official DID PLC directory. Lookup a DID to get full account history: creation date, all past handles, key rotations.
|
||||
|
||||
- [plc.directory](https://plc.directory)
|
||||
@@ -0,0 +1,138 @@
|
||||
---
|
||||
title: "X / Twitter"
|
||||
description: "Enumeration, search operators, deleted content recovery and tools for investigating X accounts."
|
||||
tags: ["osint", "twitter", "x", "social-media", "enumeration"]
|
||||
publishDate: 2026-04-29
|
||||
---
|
||||
|
||||
## Key Concepts
|
||||
|
||||
Every account has two identifiers:
|
||||
|
||||
- **Handle**: `@username` (can change)
|
||||
- **User ID**: numeric, permanent (survives handle changes and suspensions)
|
||||
|
||||
Unlike [Bluesky](/notes/osint/bluesky), X now requires a login to browse most content in the browser. The free API tier (v2) is severely limited. Most open-source scraping tools that bypassed the API (Twint, snscrape, GetOldTweets3) are broken since the 2023 API lockdown.
|
||||
|
||||
## Account Enumeration
|
||||
|
||||
### Handle to User ID
|
||||
|
||||
The user ID stays constant when someone changes their handle or gets suspended. Several web tools resolve it:
|
||||
|
||||
- [tweeterid.com](https://tweeterid.com/)
|
||||
- [commentpicker.com/twitter-id.php](https://commentpicker.com/twitter-id.php)
|
||||
|
||||
Or via the profile page source: look for `"id_str"` in the page JSON.
|
||||
|
||||
### Banner last update time
|
||||
|
||||
The profile banner URL contains a Unix timestamp indicating when the banner was last changed:
|
||||
|
||||
```
|
||||
https://pbs.twimg.com/profile_banners/{user_id}/{unix_timestamp}/600x200
|
||||
```
|
||||
|
||||
Right-click the banner image and copy the URL, or inspect the page source. Convert the timestamp at [unixtimestamp.com](https://www.unixtimestamp.com/).
|
||||
|
||||
### Timestamp from ID (Snowflake)
|
||||
|
||||
Twitter IDs are Snowflake IDs: the numeric value encodes the exact creation time of a tweet or account. Extract it with:
|
||||
|
||||
```python
|
||||
tweet_id = 1234567890123456789
|
||||
timestamp_ms = (tweet_id >> 22) + 1288834974657
|
||||
```
|
||||
|
||||
`1288834974657` is Twitter's custom epoch (Nov 4, 2010). Works on both tweet IDs and user IDs — useful to confirm account creation date without needing profile metadata.
|
||||
|
||||
Several online converters exist if you don't want to do it manually — search "snowflake id decoder".
|
||||
|
||||
### Direct profile URL by ID
|
||||
|
||||
Old tweet/profile URLs using numeric IDs still resolve even after handle changes:
|
||||
|
||||
```
|
||||
https://x.com/i/user/$USER_ID
|
||||
```
|
||||
|
||||
## Search Operators
|
||||
|
||||
Accessible at `x.com/search`. Operators are combinable.
|
||||
|
||||
| Operator | Example | Effect |
|
||||
| ----------------- | -------------------------- | ------------------------ |
|
||||
| `"..."` | `"exact phrase"` | Exact match |
|
||||
| `from:` | `from:handle` | Posts by user |
|
||||
| `to:` | `to:handle` | Posts directed at user |
|
||||
| `since:` | `since:2024-01-01` | After date (YYYY-MM-DD) |
|
||||
| `until:` | `until:2024-06-30` | Before date (YYYY-MM-DD) |
|
||||
| `lang:` | `lang:fr` | Language (ISO 639-1) |
|
||||
| `near:` | `near:"Paris" within:10km` | Geo (web only, not API) |
|
||||
| `geocode:` | `geocode:48.85,2.35,5km` | Geo by coordinates |
|
||||
| `filter:images` | | Posts with images |
|
||||
| `filter:videos` | | Posts with videos |
|
||||
| `filter:links` | | Posts with URLs |
|
||||
| `filter:verified` | | Verified accounts only |
|
||||
| `-filter:replies` | | Exclude replies |
|
||||
| `min_retweets:` | `min_retweets:100` | Engagement threshold |
|
||||
| `min_faves:` | `min_faves:500` | Engagement threshold |
|
||||
| `#tag` | `#osint` | Hashtag |
|
||||
| `-term` | `-spam` | Exclude term |
|
||||
|
||||
Boolean: spaces imply AND, use uppercase `OR` for alternatives, parentheses for grouping.
|
||||
|
||||
#### Direct search URL
|
||||
|
||||
```
|
||||
https://x.com/search?q=from%3A$HANDLE+since%3A2024-01-01&f=live
|
||||
```
|
||||
|
||||
`f=live` returns chronological results instead of relevance-ranked.
|
||||
|
||||
## Google Dorks
|
||||
|
||||
```
|
||||
site:x.com "$TARGET"
|
||||
site:twitter.com "$TARGET"
|
||||
site:x.com/i/status "$KEYWORD"
|
||||
"twitter.com/$HANDLE" OR "x.com/$HANDLE"
|
||||
```
|
||||
|
||||
Old `twitter.com` URLs are still indexed separately from `x.com`, search both.
|
||||
|
||||
## Deleted and Archived Content
|
||||
|
||||
### Wayback Machine
|
||||
|
||||
```
|
||||
https://web.archive.org/web/*/twitter.com/$HANDLE/status/*
|
||||
https://web.archive.org/web/*/x.com/$HANDLE/status/*
|
||||
```
|
||||
|
||||
Manually browse snapshots, or use [waybacktweets](https://github.com/claromes/waybacktweets) to batch-retrieve CDX data:
|
||||
|
||||
```bash
|
||||
pip install waybacktweets
|
||||
waybacktweets $HANDLE
|
||||
```
|
||||
|
||||
Outputs CSV/JSON with archived tweet URLs. Useful for deleted posts and suspended accounts.
|
||||
|
||||
### Twayback
|
||||
|
||||
Web tool wrapping the same Wayback CDX API with a UI:
|
||||
|
||||
```
|
||||
https://twayback.space/
|
||||
```
|
||||
|
||||
Note: only works if the tweet was crawled before deletion.
|
||||
|
||||
### Profile history
|
||||
|
||||
The Wayback Machine also archives profile pages: past bios, display names, profile photos, header images. Check snapshots at:
|
||||
|
||||
```
|
||||
https://web.archive.org/web/*/twitter.com/$HANDLE
|
||||
```
|
||||
@@ -1,14 +0,0 @@
|
||||
---
|
||||
title: "test2"
|
||||
description: ""
|
||||
tags: []
|
||||
publishDate: 2026-04-24
|
||||
---
|
||||
|
||||
## Notes
|
||||
|
||||
Salut comment ça va ! $test1
|
||||
|
||||
```bash
|
||||
nmap -p $test2 "$test3$test4"
|
||||
```
|
||||
@@ -1,14 +0,0 @@
|
||||
---
|
||||
title: "Bla blablalbal ae aetnaekjta ektae ktklaaek ljbtaekjb taekbt akejbt"
|
||||
description: "lorem est seot nopsejt soejtosehtose ose toiseht jophs etosh etoshte osehtosht oshe topsh etopshiospehitopsehti."
|
||||
tags: ["test", "test1", "test2"]
|
||||
publishDate: 2026-04-24
|
||||
---
|
||||
|
||||
## Notes
|
||||
|
||||
Salut comment ça va ! $test1
|
||||
|
||||
```bash
|
||||
nmap -p $port "$Ip"
|
||||
```
|
||||
+3
-5
@@ -30,10 +30,6 @@ import { House, FolderOpen } from "@lucide/astro";
|
||||
<House class="size-5" />
|
||||
Go Home
|
||||
</a>
|
||||
<a href="/projects" class="btn btn-outline gap-2">
|
||||
<FolderOpen class="size-5" />
|
||||
View Projects
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Helpful Links -->
|
||||
@@ -44,7 +40,9 @@ import { House, FolderOpen } from "@lucide/astro";
|
||||
<div class="flex gap-3 justify-center flex-wrap text-sm">
|
||||
<a href="/blog" class="link link-hover">Blog</a>
|
||||
<span class="text-base-content/30">•</span>
|
||||
<a href="/projects" class="link link-hover">All my Projects</a>
|
||||
<a href="/projects" class="link link-hover">Projects</a>
|
||||
<span class="text-base-content/30">•</span>
|
||||
<a href="/notes" class="link link-hover">Infosec Notes</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+3
-5
@@ -30,10 +30,6 @@ import { House, FolderOpen } from "@lucide/astro";
|
||||
<House class="size-5" />
|
||||
Go Home
|
||||
</a>
|
||||
<a href="/projects" class="btn btn-outline gap-2">
|
||||
<FolderOpen class="size-5" />
|
||||
View Projects
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Helpful Links -->
|
||||
@@ -44,7 +40,9 @@ import { House, FolderOpen } from "@lucide/astro";
|
||||
<div class="flex gap-3 justify-center flex-wrap text-sm">
|
||||
<a href="/blog" class="link link-hover">Blog</a>
|
||||
<span class="text-base-content/30">•</span>
|
||||
<a href="/projects" class="link link-hover">All my Projects</a>
|
||||
<a href="/projects" class="link link-hover">Projects</a>
|
||||
<span class="text-base-content/30">•</span>
|
||||
<a href="/notes" class="link link-hover">Infosec Notes</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -68,6 +68,12 @@ if (error instanceof Error) {
|
||||
rel="noopener noreferrer"
|
||||
class="link link-hover">Report Issue</a
|
||||
>
|
||||
<span class="text-base-content/30">•</span>
|
||||
<a href="/blog" class="link link-hover">Blog</a>
|
||||
<span class="text-base-content/30">•</span>
|
||||
<a href="/projects" class="link link-hover">Projects</a>
|
||||
<span class="text-base-content/30">•</span>
|
||||
<a href="/notes" class="link link-hover">Infosec Notes</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -68,6 +68,12 @@ if (error instanceof Error) {
|
||||
rel="noopener noreferrer"
|
||||
class="link link-hover">Report Issue</a
|
||||
>
|
||||
<span class="text-base-content/30">•</span>
|
||||
<a href="/blog" class="link link-hover">Blog</a>
|
||||
<span class="text-base-content/30">•</span>
|
||||
<a href="/projects" class="link link-hover">Projects</a>
|
||||
<span class="text-base-content/30">•</span>
|
||||
<a href="/notes" class="link link-hover">Infosec Notes</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -6,11 +6,7 @@ import NoteTOC from "../../components/NoteTOC.astro";
|
||||
import NoteNavSidebar from "../../components/NoteNavSidebar.svelte";
|
||||
import NoteGraphSidebar from "../../components/NoteGraphSidebar.astro";
|
||||
import NoteVars from "../../components/NoteVars.svelte";
|
||||
import {
|
||||
getCategory,
|
||||
extractLinks,
|
||||
extractHeadings,
|
||||
} from "../../utils/notes";
|
||||
import { getCategory, extractLinks, extractHeadings } from "../../utils/notes";
|
||||
|
||||
export async function getStaticPaths() {
|
||||
const notes = await getCollection("notes");
|
||||
@@ -41,7 +37,11 @@ const backlinks = allNotes.filter(
|
||||
|
||||
const graphNodes = [
|
||||
{ id: entry.id, title: entry.data.title, current: true },
|
||||
...forwardLinks.map((n) => ({ id: n.id, title: n.data.title, current: false })),
|
||||
...forwardLinks.map((n) => ({
|
||||
id: n.id,
|
||||
title: n.data.title,
|
||||
current: false,
|
||||
})),
|
||||
...backlinks
|
||||
.filter((n) => !forwardLinks.some((f) => f.id === n.id))
|
||||
.map((n) => ({ id: n.id, title: n.data.title, current: false })),
|
||||
@@ -72,7 +72,7 @@ const headings = extractHeadings(entry.body ?? "");
|
||||
</style>
|
||||
|
||||
<Layout
|
||||
title={`${entry.data.title} — Security Notes`}
|
||||
title={`${entry.data.title} - Infosec Notes`}
|
||||
description={entry.data.description}
|
||||
>
|
||||
<main class="max-w-screen-2xl mx-auto">
|
||||
@@ -92,12 +92,24 @@ const headings = extractHeadings(entry.body ?? "");
|
||||
>
|
||||
<ul>
|
||||
<li>
|
||||
<a
|
||||
href="/notes"
|
||||
class="hover:text-base-content/70"
|
||||
>notes</a>
|
||||
<a href="/notes" class="hover:text-base-content/70"
|
||||
>notes</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
{
|
||||
entry.id.includes("/") ? (
|
||||
<a
|
||||
href={`/notes/${getCategory(entry)}`}
|
||||
class="hover:text-base-content/70"
|
||||
>
|
||||
{getCategory(entry)}
|
||||
</a>
|
||||
) : (
|
||||
getCategory(entry)
|
||||
)
|
||||
}
|
||||
</li>
|
||||
<li>{getCategory(entry)}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
@@ -196,8 +208,7 @@ const headings = extractHeadings(entry.body ?? "");
|
||||
<label
|
||||
for="nav-drawer"
|
||||
aria-label="close sidebar"
|
||||
class="drawer-overlay"
|
||||
></label>
|
||||
class="drawer-overlay"></label>
|
||||
<NoteNavSidebar
|
||||
client:load
|
||||
notes={sortedNotes}
|
||||
@@ -212,8 +223,7 @@ const headings = extractHeadings(entry.body ?? "");
|
||||
<label
|
||||
for="graph-drawer"
|
||||
aria-label="close sidebar"
|
||||
class="drawer-overlay xl:hidden"
|
||||
></label>
|
||||
class="drawer-overlay xl:hidden"></label>
|
||||
<NoteGraphSidebar
|
||||
entry={entry}
|
||||
graphNodes={graphNodes}
|
||||
|
||||
@@ -0,0 +1,178 @@
|
||||
---
|
||||
import { getCollection } from "astro:content";
|
||||
import Layout from "../../layouts/Layout.astro";
|
||||
import NoteNavSidebar from "../../components/NoteNavSidebar.svelte";
|
||||
import { getCategory } from "../../utils/notes";
|
||||
import { List } from "@lucide/astro";
|
||||
|
||||
export async function getStaticPaths() {
|
||||
const notes = await getCollection("notes");
|
||||
|
||||
const folderCategories = [
|
||||
...new Set(
|
||||
notes.filter((n) => n.id.includes("/")).map((n) => getCategory(n)),
|
||||
),
|
||||
];
|
||||
|
||||
return folderCategories.map((category) => {
|
||||
const allNotes = notes.sort((a, b) =>
|
||||
a.data.title.localeCompare(b.data.title),
|
||||
);
|
||||
const categories = [...new Set(notes.map(getCategory))].sort();
|
||||
return {
|
||||
params: { category },
|
||||
props: {
|
||||
category,
|
||||
categoryNotes: notes
|
||||
.filter((n) => getCategory(n) === category)
|
||||
.sort((a, b) => a.data.title.localeCompare(b.data.title)),
|
||||
allNotes,
|
||||
categories,
|
||||
},
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
const { category, categoryNotes, allNotes, categories } = Astro.props;
|
||||
|
||||
if (!categoryNotes) {
|
||||
return new Response(null, { status: 404, statusText: "Not found" });
|
||||
}
|
||||
---
|
||||
|
||||
<style>
|
||||
.drawer.lg\:drawer-open > .drawer-side {
|
||||
top: 3rem;
|
||||
height: calc(100vh - 3rem);
|
||||
}
|
||||
</style>
|
||||
|
||||
<Layout
|
||||
title={`${category} - Infosec Notes`}
|
||||
description={`Notes on ${category}.`}
|
||||
>
|
||||
<main class="max-w-screen-2xl mx-auto">
|
||||
<div class="drawer lg:drawer-open min-h-[calc(100vh-3rem)]">
|
||||
<input id="nav-drawer" type="checkbox" class="drawer-toggle" />
|
||||
|
||||
<div class="drawer-content flex flex-col min-w-0">
|
||||
<main class="flex-1 px-4 sm:px-6 lg:px-10 py-6 lg:py-10 min-w-0">
|
||||
<div class="max-w-3xl mx-auto lg:mx-0">
|
||||
<div class="flex items-center justify-between mb-10">
|
||||
<div
|
||||
class="breadcrumbs text-xs font-mono text-base-content/35 p-0"
|
||||
>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="/notes" class="hover:text-base-content/70">notes</a
|
||||
>
|
||||
</li>
|
||||
<li class="text-base-content/60">{category}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<label
|
||||
for="nav-drawer"
|
||||
class="btn btn-ghost btn-xs lg:hidden font-mono text-base-content/40 hover:text-base-content/70 border border-base-300/50"
|
||||
>
|
||||
<List size={11} />
|
||||
nav
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="mb-10">
|
||||
<div class="flex items-baseline gap-3 mb-1">
|
||||
<h1 class="text-4xl sm:text-5xl font-bold">
|
||||
<span class="text-primary/40 font-mono mr-1">/</span>{
|
||||
category
|
||||
}
|
||||
</h1>
|
||||
</div>
|
||||
<p class="font-mono text-xs text-base-content/25 ml-8">
|
||||
{categoryNotes.length} note{
|
||||
categoryNotes.length !== 1 ? "s" : ""
|
||||
}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="border-t border-base-300/40 mb-1"></div>
|
||||
<ul class="divide-y divide-base-300/20">
|
||||
{
|
||||
categoryNotes.map((note) => (
|
||||
<li>
|
||||
<a
|
||||
href={`/notes/${note.id}`}
|
||||
class="group flex items-center gap-4 py-3 hover:bg-base-200/30 px-2 -mx-2 transition-colors"
|
||||
>
|
||||
<div class="flex-1 min-w-0">
|
||||
<div class="flex flex-col mb-0.5">
|
||||
<span class="font-semibold text-sm group-hover:text-primary transition-colors">
|
||||
{note.data.title}
|
||||
</span>
|
||||
{note.data.description && (
|
||||
<span class="text-xs text-base-content/35 truncate">
|
||||
{note.data.description}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
{note.data.tags.length > 0 && (
|
||||
<div class="flex flex-wrap gap-1 mt-1">
|
||||
{note.data.tags.map((tag) => (
|
||||
<span class="badge badge-ghost badge-xs font-mono text-base-content/30">
|
||||
{tag}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="14"
|
||||
height="14"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
class="text-base-content/20 group-hover:text-primary/50 shrink-0 transition-colors"
|
||||
>
|
||||
<path d="m9 18 6-6-6-6" />
|
||||
</svg>
|
||||
</a>
|
||||
</li>
|
||||
))
|
||||
}
|
||||
</ul>
|
||||
|
||||
<div
|
||||
class="border-t border-base-300/30 mt-12 pt-6 flex items-center justify-between font-mono text-[10px] text-base-content/25"
|
||||
>
|
||||
<a
|
||||
href="/notes"
|
||||
class="hover:text-base-content/50 transition-colors"
|
||||
>
|
||||
← all notes
|
||||
</a>
|
||||
<a href="/" class="hover:text-base-content/50 transition-colors">
|
||||
~/hadi
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<div class="drawer-side z-50">
|
||||
<label
|
||||
for="nav-drawer"
|
||||
aria-label="close sidebar"
|
||||
class="drawer-overlay"></label>
|
||||
<NoteNavSidebar
|
||||
client:load
|
||||
notes={allNotes}
|
||||
currentCategory={category}
|
||||
categories={categories}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</Layout>
|
||||
@@ -22,16 +22,23 @@ const searchNotes = sortedNotes.map((n) => ({
|
||||
---
|
||||
|
||||
<Layout
|
||||
title="Security Notes — Another Hadi"
|
||||
description="Reference notes on cybersecurity tools and techniques."
|
||||
title="Infosec Notes - Another Hadi"
|
||||
description="Cheatsheets on cybersecurity tools and techniques."
|
||||
>
|
||||
<main class="max-w-4xl mx-auto px-4 py-16 sm:py-20">
|
||||
<div class="text-center mb-12">
|
||||
<h1 class="text-4xl sm:text-5xl font-bold mb-4">Notes</h1>
|
||||
<h1 class="text-4xl sm:text-5xl font-bold mb-4">Infosec Notes</h1>
|
||||
<p class="text-xl text-base-content/70">
|
||||
Reference sheets on cybersecurity tools and techniques.
|
||||
Cheat sheets on cybersecurity tools and techniques.
|
||||
</p>
|
||||
</div>
|
||||
<div
|
||||
class="text-xs font-mono text-base-content/25 border border-base-300/30 rounded-box px-4 py-3 mb-10 max-w-xl mx-auto text-center leading-relaxed"
|
||||
>
|
||||
All content is intended for educational purposes, CTF challenges, and
|
||||
authorized penetration testing only. Do not use any of this against
|
||||
systems you do not own or have explicit permission to test.
|
||||
</div>
|
||||
<NotesSearch client:load notes={searchNotes} />
|
||||
</main>
|
||||
</Layout>
|
||||
|
||||
Reference in New Issue
Block a user