Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,20 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ExtSentry - Browser Extension Threat Intelligence</title>
<meta name="description" content="Community-driven browser extension threat intelligence. IOC feeds for malicious and sensitive browser extensions across 16+ security platform formats.">
<meta property="og:type" content="website">
<meta property="og:site_name" content="ExtSentry">
<meta property="og:url" content="https://extsentry.github.io/">
<meta property="og:title" content="ExtSentry - Browser Extension Threat Intelligence">
<meta property="og:description" content="Community-driven browser extension threat intelligence. IOC feeds for malicious and sensitive browser extensions across 16+ security platform formats.">
<meta property="og:image" content="https://extsentry.github.io/og-image.png">
<meta property="og:image:width" content="1200">
<meta property="og:image:height" content="630">
<meta property="og:image:type" content="image/png">
<meta property="og:locale" content="en_US">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="ExtSentry - Browser Extension Threat Intelligence">
<meta name="twitter:description" content="Community-driven browser extension threat intelligence. IOC feeds for malicious and sensitive browser extensions across 16+ security platform formats.">
<meta name="twitter:image" content="https://extsentry.github.io/og-image.png">
<link rel="icon" type="image/png" href="data:image/png;base64,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">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;500;600;700&family=IBM+Plex+Sans:wght@400;500;600;700;800&display=swap" rel="stylesheet">
Expand Down
Binary file added og-image.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
168 changes: 168 additions & 0 deletions scripts/gen-og-image.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
#!/usr/bin/env python3
"""Generate og-image.png for ExtSentry (extsentry.github.io).

Browser Extension Threat Intelligence — dark security-console card:
- near-black ground (#0a0c10, matches index.html <body>), radial red-orange glow
- glowing fiery puzzle-piece "sentry" mark (brand: demon puzzle piece w/ eye slits)
- big wordmark, mono tagline, severity/IOC chip row, footer strip

Run from the repo root: python3 scripts/gen-og-image.py
"""
import os

from PIL import Image, ImageDraw, ImageFont

W, H = 1200, 630

GROUND = "#0a0c10"
PANEL = "#12161d"
PANEL_HI = "#1b212b"
LINE = "#262d38"
INK = "#e6edf3"
INK_2 = "#c8ccd4"
INK_3 = "#8b949e"
ORANGE = "#ff7a18"
RED = "#e0483e"
EMBER = "#ffb066"
BLUE = "#58a6ff"

FONT_DIRS = [
"/usr/share/fonts/truetype/dejavu",
"/usr/share/fonts/truetype/noto",
]


def font(name, size):
for d in FONT_DIRS:
p = os.path.join(d, name)
if os.path.exists(p):
return ImageFont.truetype(p, size)
raise FileNotFoundError(f"font {name} not found")


def rounded_rect(draw, box, r, fill, outline=None, width=1):
draw.rounded_rectangle(box, radius=r, fill=fill, outline=outline, width=width)


def draw_puzzle_mark(draw, cx, cy, s, glow=True):
"""Fiery puzzle piece with slitted eyes + jagged grin (brand motif)."""
# outer glow
if glow:
halo = Image.new("RGBA", (W, H), (0, 0, 0, 0))
hd = ImageDraw.Draw(halo)
hd.ellipse((cx - s * 1.05, cy - s * 1.05, cx + s * 1.05, cy + s * 1.05),
fill=(255, 122, 24, 40))
return halo

# puzzle piece body: rounded square with left tab + right notch
x0, y0, x1, y1 = cx - s, cy - s, cx + s, cy + s
tab_w, tab_h = int(s * 0.34), int(s * 0.30)
notch_w, notch_h = int(s * 0.26), int(s * 0.26)

# body path via rounded_rectangle chunks (PIL has no union; draw layered)
rounded_rect(draw, (x0, y0 + tab_h, x1, y1 - notch_h), int(s * 0.16),
fill=RED, outline=ORANGE, width=3)
# left tab
rounded_rect(draw, (x0 - tab_w, y0 + tab_h, x0 + tab_w, y1 - tab_h - int(s * 0.30)),
int(s * 0.12), fill=RED, outline=ORANGE, width=3)
# right notch (cut out): cover with ground-colored rounded rect
rounded_rect(draw, (x1 - notch_w - 2, cy - notch_h, x1 + 2, cy + notch_h),
int(s * 0.08), fill=PANEL, outline=PANEL)

# gradient-ish top highlight
rounded_rect(draw, (x0 + 22, y0 + tab_h + 22, x1 - 22, y0 + tab_h + 62),
int(s * 0.08), fill=EMBER)

# two slitted angry eyes
eye_dx = int(s * 0.30)
eye_y = cy - int(s * 0.28)
for ex in (cx - eye_dx, cx + eye_dx):
draw.polygon([(ex - 24, eye_y + 14), (ex, eye_y - 12), (ex + 24, eye_y + 14),
(ex + 10, eye_y + 22), (ex - 10, eye_y + 22)], fill=INK)
draw.polygon([(ex - 16, eye_y + 10), (ex, eye_y - 6), (ex + 16, eye_y + 10),
(ex + 8, eye_y + 16), (ex - 8, eye_y + 16)], fill=ORANGE)

# jagged grin
gx0, gx1 = cx - int(s * 0.44), cx + int(s * 0.44)
gy = cy + int(s * 0.44)
teeth = 9
pts = [(gx0, gy)]
for i in range(teeth):
px = gx0 + (gx1 - gx0) * (i + 0.5) / teeth
pts.append((px, gy + 16))
pts.append((gx1, gy))
pts.append((gx1, gy + 2))
for i in range(teeth - 1, -1, -1):
px = gx0 + (gx1 - gx0) * (i + 0.5) / teeth
pts.append((px, gy + 22))
pts.append((gx0, gy + 2))
draw.polygon(pts, fill="#000000")


def main():
img = Image.new("RGB", (W, H), GROUND)
draw = ImageDraw.Draw(img)

# top scan bar (console vibe)
draw.rectangle((0, 0, W, 6), fill=ORANGE)

# halo behind mark
halo = draw_puzzle_mark(draw, 300, 300, 180, glow=True)
img = Image.alpha_composite(img.convert("RGBA"), halo).convert("RGB")
draw = ImageDraw.Draw(img)

# mark itself
draw_puzzle_mark(draw, 300, 300, 180, glow=False)

# mono kicker chip
kf = font("DejaVuSansMono-Bold.ttf", 22)
kicker = "THREAT INTELLIGENCE FEED"
draw.rounded_rectangle((470, 150, 470 + kf.getlength(kicker) + 40, 196), 12,
fill=PANEL, outline=LINE, width=2)
draw.text((490, 162), kicker, font=kf, fill=ORANGE)

# wordmark
wf = font("DejaVuSans-Bold.ttf", 104)
draw.text((466, 210), "ExtSentry", font=wf, fill=INK)

# tagline
tf = font("DejaVuSans-Bold.ttf", 28)
draw.text((466, 336), "Browser Extension Threat Intelligence", font=tf, fill=INK_2)

# mono sub-line
sf = font("DejaVuSansMono.ttf", 22)
draw.text((468, 390), "IOC feeds for malicious & sensitive extensions", font=sf, fill=INK_3)

# dashed separator
y = 448
x = 468
while x < 1110:
draw.rectangle((x, y, x + 26, y + 4), fill=LINE)
x += 42

# chip row
chips = [("16+ FEED FORMATS", ORANGE), ("COMMUNITY-DRIVEN", BLUE), ("FREE & OPEN", EMBER)]
chip_y = 478
cx = 466
cf = font("DejaVuSans-Bold.ttf", 18)
for label, color in chips:
w = int(cf.getlength(label)) + 44
draw.rounded_rectangle((cx, chip_y, cx + w, chip_y + 42), 10,
fill=PANEL_HI, outline=color, width=2)
draw.text((cx + 22, chip_y + 10), label, font=cf, fill=color)
cx += w + 16

# footer strip
draw.rectangle((0, 566, W, 630), fill=PANEL)
ff = font("DejaVuSansMono.ttf", 22)
draw.text((60, 584), "extsentry.github.io", font=ff, fill=INK_3)
draw.text((W - 60 - ff.getlength("browser extension threat intelligence"),
584), "browser extension threat intelligence", font=ff, fill=INK_3)

out = "og-image.png"
img.save(out, "PNG")
print(f"wrote {out}: {img.size[0]}x{img.size[1]}")


if __name__ == "__main__":
main()