Commit Graph

236 Commits

Author SHA1 Message Date
CarterPerez-dev 2a2a3c4d8f feat(canary): envfile generator (shuffled bait + embedded canary URL)
Generator builds the artifact per spec §9.6:

  - extractIncludeKeys(t.Metadata) reads include_keys from JSON
    metadata, defaults to ["aws", "db"] on missing/malformed metadata
    (defensive fallback covers empty raw, invalid JSON, wrong type,
    null value, empty array, missing field, all-whitespace entries)
  - buildSections iterates recipe keys, appending the canary section
    last: INTERNAL_METRICS_ENDPOINT=<baseURL>/c/<t.ID> +
    INTERNAL_METRICS_TOKEN=tok_live_<32-char alnum>
  - shuffleSections uses crypto/rand-driven Fisher-Yates so the canary
    section can land at any position. Spec §9.6 line 1301 uses
    math/rand.Shuffle; we use crypto/rand to stay consistent with the
    Phase 0 supplement choice and the recipes package
  - renderSections writes the fixed "# Production environment / NODE_ENV
    / PORT" header followed by each section's comment + key=value lines
  - Artifact: KindText, ContentType "text/plain; charset=utf-8",
    Filename default ".env"

Trigger is a byte-identical content-copy of webbug/docx/pdf: 200 +
pixel.Clone() GIF + cache headers + realIP triplet + nil-token-returns-
GIF defense-in-depth.

Tests (~30 cases):
  - Generator: Type, KindText, ContentType, Filename defaulting (4),
    header presence, canary URL embedded, canary token format
    (tok_live_+32 alnum regex), trailing-slash trim, default
    include_keys vs metadata-driven include_keys, unknown keys
    skipped, 6 malformed-metadata fallback subcases, empty-string
    filtering in keys list, shuffle variability (30 invocations →
    canary at multiple positions), distinct outputs (random bait +
    random shuffle), token id appears exactly once in output
  - Trigger: GIF response shape, source-IP precedence (11 subcases
    matching docx/pdf/kubeconfig), missing UA/Referer → nil pointers,
    response body independence (mutate one, other unchanged),
    nil-token still returns GIF + nil event

Registry wire-up next.
2026-05-13 14:36:06 -04:00
CarterPerez-dev 003aa9970c feat(canary): envfile bait recipes (aws/stripe/github/db)
Phase 7 first commit. Four recipes for generating realistic-looking
credentials that pass syntactic validation by tools like gitleaks but
are NOT real credentials — they're decoration that makes the env file
look like a legitimate prod config dump. The detection mechanism is
the canary URL embedded by the generator (next commit); the bait keys
themselves don't trigger anything.

Package layout (sub-package under envfile/):
  - recipes.go: EnvLine, Recipe interface, internal registry, shared
    crypto/rand helpers (RandomAlnumUpper / RandomAlnumMixed /
    RandomHexLower / RandomBase64 / RandomChoice). All randomness
    uses crypto/rand — no math/rand or gosec G404 fight, matches the
    Phase 0 supplement's discipline.
  - aws.go: AKIA + 16 [A-Z0-9] body (gitleaks regex
    `AKIA[0-9A-Z]{16}`) + 40+ base64-encoded secret + canonical
    region (us-east-1 etc.) + stable S3 bucket name
  - stripe.go: sk_live_/pk_live_/whsec_ prefixes with 24/24/32-char
    [A-Za-z0-9] bodies (matches Stripe's documented live-key format)
  - github.go: ghp_ + 36 base62 body + 6 base62 "checksum" = 42 chars
    after the prefix (matches gitleaks
    `ghp_[0-9a-zA-Z]{36,255}`); plus base64 deploy-key, stable owner +
    repo names (acme-corp / internal-platform)
  - db.go: postgres://app_writer:<24-char pass>@db.internal:5432/
    app_prod?sslmode=require + redis://default:<32-char pass>@
    cache-prod.internal:6379/0 — realistic internal-DNS-style
    hostnames per spec §9.6 "Bait realism principles"

Public API:
  - recipes.Get(key) (Recipe, bool) — registry lookup
  - recipes.AvailableKeys() []string — sorted snapshot
  - Recipe types (AWS, Stripe, GitHub, DB) exported so callers can
    construct them directly if needed (registry is the normal path)

Tests (~30 cases): one test file per recipe plus shared helpers.
  - Format-regex assertions: AKIA + base32-upper, sk_live_/pk_live_/
    whsec_ + base62, ghp_ + base62, region xx-name-n
  - URL parsing for postgres + redis (real net/url validation, not
    string matching)
  - Distinct-invocations checks (20 calls → near-20 unique secrets)
  - Helper coverage: RandomAlnumUpper/Mixed/HexLower/Base64/Choice
    length + alphabet + empty-input handling

Recipes package compiles + tests pass in isolation. Generator and
registry wire-up land in next commits.

go test -race -timeout=60s ./internal/token/generators/envfile/recipes/...
passes with 0 lint issues.
2026-05-13 14:33:16 -04:00
CarterPerez-dev 982beafdc9 fix(canary-phase6): address audit nit before rollup
Phase 6 audit (2 agents in parallel) returned PASS. The
general-purpose spec-adherence agent flagged one actionable NIT (the
superpowers:code-reviewer flagged the same item as defensible — fixing
it per the fix-in-phase rule regardless).

N1 — template.yaml.tmpl line 2 file-header comment read
"# template.yaml" but the on-disk filename is template.yaml.tmpl
(renamed in commit 6f994f37 to avoid the repo's check-yaml pre-commit
hook). Project file-header rule is "©AngelaMos | 2026 + filename only
(NOT path)" — the comment must match the actual filename. Updated to
"# template.yaml.tmpl".

Other audit observations marked NOT-ACTIONABLE:
- NIT (verb table): verbFromMethod handles GET/POST/PUT/PATCH/DELETE
  + default-to-list. OPTIONS/CONNECT/TRACE fall through to "list"
  which is the sanctioned ceiling per the verb-derivation enhancement
  scope. Phase 14 may revisit if scan-OPTIONS forensics matters.
- NIT (defaultResource stutter): "resource is forbidden: ... cannot
  list resource resource ..." for empty-path edge cases. Real kubectl
  never emits an empty path; cosmetic only.
- NIT (empty baseURL): TrimRight on empty string produces empty +
  "/k/" + id which is relative. Registry wire-up guarantees non-empty
  baseURL; defensive guard would be belt-and-suspenders only.
- INFO (Bearer extraction): plan task 6.2 mentions "extracts Bearer
  (or path id)" — Phase 6's generator receives *token.Token
  already-resolved per the §6.2 Generator interface, so the path/Bearer
  dispatch lands at the routing layer in Phase 9. Phase 6 punt is
  legitimate.
- POSITIVE observations from both agents are retained as-is.

Pre-rollup gate clean: go build / vet / test -race / test -tags=
integration / golangci-lint run all pass.
2026-05-13 14:26:15 -04:00
CarterPerez-dev ceec4d37d1 feat(canary): register kubeconfig generator in registry
Fifth entry in the registry map. Cardinality bumps 4 → 5; pending list
shrinks to {envfile, mysql} (2 remaining).

  - registry.go: adds the import + token.TypeKubeconfig: kubeconfig.New()
  - registry_test.go:
      - TestBuild_RegistersKubeconfig added (mirrors RegistersPDF)
      - TypeKubeconfig removed from TestBuild_PendingTypesNotYetRegistered
      - TestBuild_OnlyExpectedTypesPresentInPhase5 renamed → InPhase6,
        cardinality assertion bumped 4 → 5

Pre-audit gate clean at HEAD: go build ./... + go vet ./... + go test
-race -timeout=60s ./... + go test -tags=integration -race -timeout=300s
./internal/token/... ./internal/event/... + golangci-lint run ./...
all pass with 0 issues.
2026-05-13 14:22:32 -04:00
CarterPerez-dev bd0c664602 feat(canary): kubeconfig fake K8s API handler (forbidden response)
Trigger implementation completes the kubeconfig Generator's interface
conformance. Behavior matches spec §9.5 + §8.5 defense-in-depth:

  - ALWAYS returns 403 + Kubernetes-shaped Status JSON (whether the
    token is valid or not). Attackers cannot distinguish "valid
    token, no permission" from "no such token" by status code or
    response shape — both are 403 with byte-identical structure modulo
    the resource/verb message slot.
  - Status JSON conforms to k8s.io Status object: kind=Status,
    apiVersion=v1, metadata={}, status=Failure, reason=Forbidden,
    code=403, message=verisimilar real-kubectl error string.
  - Message format mirrors kubectl's actual output:
      <resource> is forbidden: User "system:anonymous" cannot <verb>
      resource "<resource>" in API group "" in the namespace "default"
    where <resource> is the last segment of r.URL.Path and <verb> is
    derived from the HTTP method (GET/HEAD → list, POST → create, PUT
    → update, PATCH → patch, DELETE → delete, others → list). This is
    richer than spec §9.5's hardcoded "list" — kubectl ships the verb
    based on the action it's attempting, so a /apis/.../pods DELETE
    response saying "cannot list" would tip the attacker off. Method
    mapping is forensic gold for the operator (which kubectl action
    actually fired) and verisimilar to the attacker.
  - Event.Extra captures the kubectl_path / kubectl_method /
    kubectl_query / kubectl_ua fields per spec §9.5 line 1234. UA
    capture is the forensic prize — kubectl ships its version + arch
    in the UA string ("kubectl/v1.30.0 (linux/amd64) kubernetes/...").
  - Source-IP triplet (realIP / lastNonEmptyXFF / optionalHeader) is
    a content-copy of webbug/docx/pdf — sanctioned duplication per
    Phase 2/3/4 anti-relitigation set. Phase 9 middleware extraction
    collapses these into one shared helper.
  - Nil-token path returns the same 403+Status with verisimilar
    message + headers, and nil event so the handler cannot persist
    a row with empty TokenID (FK violation) — spec §8.5.

Tests cover (~36 cases including subtests):
  - 403 + JSON content-type + cache headers
  - Status JSON shape (Kind/APIVersion/metadata={}/Status/Reason/Code)
  - Message resource extraction: pods, secrets, single-resource get,
    API version probe, trailing slash, healthz probe (6 paths)
  - Verb derivation from HTTP method (7 methods, 5 distinct verbs)
  - User "system:anonymous" impersonation present
  - kubectl_path/method/query/ua in Extra JSON
  - Empty query string handled
  - Source-IP precedence (11 subcases mirroring docx/pdf for parity)
  - Missing UA/Referer → nil pointers
  - Nil-token defense-in-depth: still returns 403 + parseable Status,
    nil event, response-shape parity with valid-token case (HTTP code
    + JSON kind/version/status/reason/code all identical so an
    attacker cannot probe token validity by diffing responses)

Generator now satisfies generators.Generator interface. Registry wire-up
in next commit.
2026-05-13 14:22:19 -04:00
CarterPerez-dev 6f994f3756 feat(canary): kubeconfig generator + YAML template
Phase 6 first commit. Generator produces a real-looking kubeconfig
artifact that points kubectl at the canary's /k/{id} endpoint:

  - text/template embedded YAML following the spec §9.5 layout (one
    cluster + one context + one user, all referencing the same
    prod-cluster + svc-backup-reader names for verisimilitude)
  - APIServerURL = baseURL + "/k/" + t.ID (trailing-slash safe via
    strings.TrimRight)
  - Token = t.ID embedded as the user's bearer token — when kubectl
    fires a request, the bearer in the Authorization header is exactly
    this id, giving us a second lookup path beyond the URL path
  - ClusterName = "prod-cluster", UserName = "svc-backup-reader" —
    hardcoded per spec §9.5 lines 1193-1194 (tempting bait names that
    look like a real prod service-account kubeconfig)
  - Artifact.Kind = KindText (different from docx/pdf KindFile — YAML
    is text)
  - ContentType "application/yaml"
  - Filename default "kubeconfig" with the standard *string deref+trim
    pattern from docx/pdf

The template file is named template.yaml.tmpl (not .yaml) so the
repo's pre-commit check-yaml hook does not parse it as pure YAML —
the {{.X}} Go-template tokens are not valid YAML flow mappings and
would fail the parser. .tmpl is the conventional extension for
Go-templated source files; identify-cli (which check-yaml uses to
match files) does not classify .yaml.tmpl as YAML. Future phases
needing templated YAML (envfile recipes, etc.) should follow the
same convention.

Tests parse the rendered output through gopkg.in/yaml.v3 against a
typed schema struct, asserting every field flows through correctly:
APIVersion, Kind, current-context, Clusters[0].name + .cluster.server,
Contexts[0].name + .context.cluster + .context.user, Users[0].name +
.user.token. Plus Filename defaulting subtests, base-URL
trailing-slash trim, subpath preserve, distinct-ids → distinct
outputs, bearer-token-embedded check, ends-with-newline check.

Handler (Trigger) ships in the next commit per spec §9.5 file split
({generator, handler, template}). At this commit kubeconfig.Generator
has Type() + Generate() but no Trigger() — the package compiles in
isolation but does not yet satisfy generators.Generator interface.
The registry wires it in commit 3.

go test -race -timeout=60s ./internal/token/generators/kubeconfig/...
passes.
2026-05-13 14:21:57 -04:00
CarterPerez-dev 73dd837362 feat(canary): register pdf generator in registry
Fourth entry in the registry map. Cardinality bumps 3 → 4; pending list
shrinks to {kubeconfig, envfile, mysql} (3 remaining).

  - registry.go: adds the import + token.TypePDF: pdf.New() entry
  - registry_test.go:
      - TestBuild_RegistersPDF added (mirrors RegistersDocx)
      - TypePDF removed from TestBuild_PendingTypesNotYetRegistered
      - TestBuild_OnlyExpectedTypesPresentInPhase4 renamed → InPhase5,
        cardinality assertion bumped 3 → 4

Pre-audit gate clean at HEAD: go build ./... + go vet ./... + go test
-race -timeout=60s ./... + go test -tags=integration -race -timeout=300s
./internal/token/... ./internal/event/... + golangci-lint run ./... all
pass with 0 issues.
2026-05-13 13:54:33 -04:00
CarterPerez-dev 13cd6cd1a3 feat(canary): pdf generator (byte-replace fixed-width URI action)
Generator runs at Token-create time. Substitutes the 76-byte placeholder
in the embedded template.pdf with the canary trigger URL padded to the
same byte length. Length preservation is critical: the PDF's xref table
indexes objects by byte offset, so anything that shifts bytes after the
catalog corrupts the file.

Algorithm (spec §9.4 lines 1145-1165):

  triggerURL := strings.TrimRight(baseURL, "/") + "/c/" + t.ID
  if len(triggerURL) > 76 → ErrTriggerURLTooLong
  padded := triggerURL + strings.Repeat("_", 76 - len(triggerURL))
  out := bytes.Replace(pdfTemplate, placeholder, padded, 1)

Defensive checks after substitution:
  - len(out) == len(pdfTemplate) (xref offsets stay valid)
  - bytes.Contains(out, triggerURL) (substitution actually happened)

Trigger is a content-copy of webbug's response (sanctioned per Phase
2/3/4 anti-relitigation set): 200 + 43-byte pixel.Clone() + Cache-Control
+ Pragma headers. Nil-token path returns the same artifact shape with no
event (spec §8.5 defense-in-depth). realIP / lastNonEmptyXFF /
optionalHeader are duplicated byte-identically with docx and slowredirect
— Phase 9 collapses this into a middleware helper.

Test suite (~32 cases, mirrors the docx Phase 4 shape plus PDF-specific
structural guards):

  Template invariants (5):
    - placeholder root byte-locatable exactly once (literal, not encoded)
    - full 76-byte placeholder byte-locatable exactly once
    - starts with %PDF- header
    - ends with %%EOF marker
    - pdfcpu.api.Validate passes on the raw template

  Generator behavior (10):
    - Type() is TypePDF, Artifact.Kind is KindFile
    - ContentType is application/pdf
    - Filename defaulting (nil / empty / whitespace / set)
    - trigger URL precedence (trailing-slash trim, subpath preserve,
      distinct ids → distinct outputs)
    - output length == template length (xref preservation)
    - output contains trigger URL
    - placeholder root removed from output
    - URL > 76 chars returns ErrTriggerURLTooLong (errors.Is)
    - boundary URL at exactly 76 chars succeeds (zero padding)
    - api.Validate still passes on substituted output
    - substitution preserves byte offset (URL occupies the same position
      the placeholder did)

  Trigger behavior (~16 incl subtests):
    - response shape identical to webbug (200 + pixel.Clone + headers)
    - records event with token id / source IP / UA / Referer
    - source-IP precedence (CF > XFF rightmost > XRI > RemoteAddr),
      11 subcases mirroring docx + webbug for full symmetry
    - missing UA/Referer → nil pointers
    - per-call body is an independent slice (mutate one, other unchanged)
    - nil-token path returns GIF + nil event (FK-safe)

go test -race -timeout=60s ./internal/token/generators/pdf/... passes.
2026-05-13 13:54:24 -04:00
CarterPerez-dev c9c9333294 feat(canary): pdf template (binary blob + build helper)
Phase 5 first commit. Adds the committed template.pdf binary blob plus
the pure-Go one-shot utility that produced it, matching the docx Phase
4 pattern (cmd/build<format>template/main.go excluded from production).

cmd/buildpdftemplate/main.go assembles a minimal PDF-1.4 by hand:

  - Header: %PDF-1.4 + 4 high-bit bytes to mark as binary
  - Object 1: Catalog → Pages root
  - Object 2: Pages → single Kids ref + Count 1
  - Object 3: Page with /AA << /O << /Type /Action /S /URI /URI (...) >> >>
    where the URI value is the 76-char placeholder
    HONEY_TRACK_URL_PADDED_TO_FIXED_WIDTH______________________________________
  - xref table with computed byte offsets
  - trailer + startxref + %%EOF

The placeholder is a direct dictionary value (NOT inside a stream), so
substitution can be a plain byte-replace at runtime without breaking the
cross-reference table — spec §9.4 line 1133. No FlateDecode anywhere.

pdfcpu (v0.12.1) is used for validation only — the builder calls
api.Validate before writing, and tests will call it again on substituted
output to confirm the PDF stays well-formed.

Verification at HEAD:
  - 477-byte template.pdf
  - sha256: 70c6359016ceb780539f8c4497991b98ff82201e35a38d36090d88856027e103
  - Reproducible: rebuild produces byte-identical output
  - grep -aob HONEY_TRACK_URL → one offset (240)
  - file: PDF document, version 1.4, 1 page(s)
  - api.Validate passes

go.mod adds pdfcpu as a direct dep with its transitive deps via go mod
tidy. golang.org/x/crypto returns to direct (pdfcpu uses it for AES
encryption support).
2026-05-13 13:54:06 -04:00
CarterPerez-dev 13b2604f18 fix(canary): replace //nolint pragmas with explicit error handling
The standing "no //nolint anywhere" rule (handoff anti-relitigation
set) was honored by Phases 2-4 but inherited template code in
core/database.go, middleware/ratelimit.go, and health/handler.go
carried 6 pragmas from the original template import. Replacing each
with proper error handling rather than silencing the linter:

core/database.go
  NewDatabase ping-failure path: propagates db.Close() error via
  multi-%w fmt.Errorf rather than discarding it via _ = db.Close().
  Pattern matches the existing rollback error wrap on line 102.

  InTx + InTxWithOptions panic-recovery defer: rollback failure now
  logs via slog.Error before re-panicking, rather than discarding
  the error via _ = tx.Rollback(). The original error context is
  preserved by the panic; the rollback failure is observable.

  jitteredDuration: switches from math/rand/v2 to crypto/rand +
  math/big. The function runs once per pool init at startup so the
  per-call cost (~microseconds) is irrelevant. Eliminates the gosec
  G404 trigger without a global exclude (handoff anti-relitigation:
  "don't add new excludes for one-off issues"). Adds the missing
  maxJitter <= 0 guard that the rand.Int64N call would have panicked
  on with a base smaller than 7ns.

middleware/ratelimit.go
  writeRateLimitExceeded: json.NewEncoder().Encode error is now
  logged via slog.Error rather than discarded. slog is already
  imported and used elsewhere in the file (line 60).

health/handler.go
  writeStatus: same pattern; adds log/slog import. Brings the file
  in line with the rest of the codebase's slog-everywhere discipline.

After: zero //nolint pragmas anywhere in backend Go code
(grep -rn "//nolint" returns nothing). golangci-lint run ./... reports
0 issues. All unit tests pass under -race; all integration tests pass
under -race -tags=integration with the testcontainer Postgres.

Surfaced by the pre-Phase-5 cross-phase alignment audit. Cleared as
part of finishing the Phase 0 lint-discipline alignment so Phases 5+
inherit a fully consistent codebase.
2026-05-13 13:28:44 -04:00
CarterPerez-dev fdc4144cbc chore(canary): drop unused auth error helpers from core
Auth-template residue in core/errors.go + core/response.go:

  - UnauthorizedError() / ForbiddenError() + Err{Unauthorized,Forbidden}
    sentinels
  - TokenExpiredError() / TokenInvalidError() / TokenRevokedError() +
    Err{TokenExpired,TokenInvalid,TokenRevoked} sentinels
  - core.Unauthorized() / core.Forbidden() response wrappers

Zero call sites anywhere in the codebase. Worse, the Token*Error names
semantically collide with canary tokens (same package, completely
unrelated meaning) — a future-reader trap that would compound once
Phase 9 wires real operator-bearer auth gates on the admin handler.

Generic AppError plumbing (NewAppError, NotFoundError, DuplicateError,
ValidationError, InternalError, RateLimitError, IsAppError, GetAppError)
is preserved — all in active use.

When Phase 9 needs operator-bearer or turnstile-gated responses, helpers
can be reintroduced with names appropriate to the actual auth design
rather than carrying generic JWT-shaped vocabulary forward into a
canary-token namespace.

Surfaced by the pre-Phase-5 cross-phase alignment audit. Cleared per
the fix-in-phase rule. BACKLOG closed section records the resolution.
2026-05-13 13:28:30 -04:00
CarterPerez-dev 31f1fff343 chore(canary): drop unused argon2 password machinery from core
internal/core/security.go was preserved through Phase 0's auth prune
untouched. Audit: zero callers anywhere in the codebase, yet the file
kept golang.org/x/crypto as a direct dep and ran a full Argon2id KDF
on every process start via a package-level init() that pre-computed
a dummy hash "to prevent timing attacks" — wasted boot CPU for an
unused codepath.

Deleted symbols:
  - HashPassword, VerifyPassword, VerifyPasswordWithRehash
  - VerifyPasswordTimingSafe + dummyHash init()
  - decodeHash, needsRehash internal helpers
  - GenerateSecureToken, GenerateRefreshToken
  - HashToken, CompareTokenHash

go mod tidy demotes golang.org/x/crypto from direct to indirect
(pgx/v5 still pulls it transitively).

Surfaced by the pre-Phase-5 cross-phase alignment audit. Cleared as
part of finishing Phase 0's auth prune rather than logged as deferred
work. BACKLOG closed section records the item + resolution.
2026-05-13 13:28:17 -04:00
CarterPerez-dev 10ae118d53 fix(canary-phase4): address audit nits before rollup
Phase 4 audit (2 agents) returned PASS with three NITs. N1 + N2 cleared
in-phase per the standing fix-in-phase rule. N3 was stylistic-consistency
positive and not actionable.

N1 — generator_test.go IP-precedence subtests: added two missing cases
present in webbug's identical suite (the realIP helper triplet is byte-
identical to webbug, so this is symmetry rather than coverage):
- "RemoteAddr loopback IPv6 strips brackets and port" → "[::1]:9999"
- "XFF IPv6 rightmost" → "198.51.100.1, 2001:db8::dead"

N2 — generator.go patchTemplate loop: the second `cErr := ...` shadowed
nothing but reused the name from the close-error block earlier in the
same iteration. Renamed the header-create variant to `hErr` for clarity.

Audit verdicts:
- superpowers:code-reviewer: PASS (0 B / 0 S / 3 N)
- general-purpose spec-adherence vs §9.3 / §8.5: PASS (0 violations,
  6/6 invariants MATCH, BACKLOG.md still empty)
2026-05-13 03:19:13 -04:00
CarterPerez-dev 5884f98616 feat(canary): register docx generator in registry
Phase 4 Task 4.5 — adds token.TypeDocx: docx.New() to registry.Build.

Test updates:
- TestBuild_RegistersDocx: new, mirrors the webbug + slowredirect
  registration tests
- TestBuild_PendingTypesNotYetRegistered: drops TypeDocx from the
  pending list (4 remain: pdf, kubeconfig, envfile, mysql)
- TestBuild_OnlyExpectedTypesPresentInPhase3 →
  TestBuild_OnlyExpectedTypesPresentInPhase4: cardinality assertion
  bumped from 2 to 3
2026-05-13 03:15:00 -04:00
CarterPerez-dev 82bb3a9f14 feat(canary): docx generator (zip-patch INCLUDEPICTURE)
Phase 4 Task 4.3+4.4 — docx Generator that produces a tracked Word
document by embedding template.docx and runtime-substituting the
HONEY_TRACK_URL placeholder in word/footer2.xml with the per-token
trigger URL. Preserves per-entry zip Method (STORE/DEFLATE) so
Word/LibreOffice still opens the result; non-footer entry bodies are
byte-identical to the template.

Generate returns {Kind: KindFile, Filename: t.Filename ?? "Document.docx",
Content: <patched zip>, ContentType: wordprocessingml MIME}. The
spec §9.3 reference snippet uses `t.Filename == ""` which won't
compile against the real schema — Token.Filename is *string. The
generator dereferences safely via resolveFilename, trimming and
falling back to the default for nil/empty/whitespace pointers.

Trigger mirrors webbug exactly per spec §9.3 line 1118: 200 + 43-byte
transparent GIF via pixel.Clone() + cache-control no-store + pragma
no-cache. Nil-token returns the same response with nil event
(spec §8.5 — no token-existence enumeration). realIP /
lastNonEmptyXFF / optionalHeader helpers are copied from webbug per
the standing rule (sanctioned duplication until Phase 9 middleware
extraction).

24 test cases including the six prescribed by implementation plan
§4.3 (OutputIsValidZip, FooterContainsTriggerURL,
FooterDoesNotContainPlaceholder, OtherEntriesUnchanged,
PreservesCompressionMethods, ReturnsGIFLikeWebbug) plus surrounding
correctness/regression coverage (type, kind, content type, filename
defaulting incl. whitespace, trigger-URL trailing-slash + subpath +
uniqueness, source-IP precedence with 9 subtests mirroring webbug,
GIF body independence per Trigger call, nil-token defense).

Also fixes pre-existing lint debt in cmd/builddocxtemplate from
commit f36cce9a: errcheck on the deferred f.Close (now propagates
via named return) and gosec G304 on os.OpenFile (now filepath.Clean
on the operator-supplied -out path). Rebuilding template.docx after
this change produces byte-identical output (verified via sha256sum).
2026-05-13 03:14:12 -04:00
CarterPerez-dev f36cce9a01 feat(canary): docx template (binary blob + build helper)
Phase 4 Task 4.1+4.2 — pure-Go OOXML template builder under
backend/cmd/builddocxtemplate/. Run:

  go run ./cmd/builddocxtemplate \
    -out ./internal/token/generators/docx/template/template.docx

produces a minimal valid .docx (5 zip entries, mixed STORE/DEFLATE
compression) whose word/footer2.xml carries an INCLUDEPICTURE field
referencing the literal placeholder string HONEY_TRACK_URL with the
\\d switch (forces fetch-on-open, no local cache).

The committed template.docx is the embedded source for the Phase 4
docx generator (next commit). Mixed compression methods in the template
make Task 4.3's TestGenerate_PreservesCompressionMethods a meaningful
regression guard — a hardcode-DEFLATE generator would now mismatch on
[Content_Types].xml + word/_rels/document.xml.rels (both STORE).

cmd/builddocxtemplate is excluded from the production binary (separate
cmd/ subdir from cmd/canary).
2026-05-13 03:04:36 -04:00
Carter Perez 95d9cd4202
Merge pull request #222 from CarterPerez-dev/dependabot/uv/PROJECTS/beginner/c2-beacon/backend/python-multipart-0.0.27
chore(deps): bump python-multipart from 0.0.26 to 0.0.27 in /PROJECTS/beginner/c2-beacon/backend
2026-05-13 02:41:07 -04:00
Carter Perez 430c16d63e
Merge pull request #230 from CarterPerez-dev/dependabot/uv/PROJECTS/beginner/c2-beacon/backend/urllib3-2.7.0
chore(deps): bump urllib3 from 2.6.3 to 2.7.0 in /PROJECTS/beginner/c2-beacon/backend
2026-05-13 02:40:31 -04:00
dependabot[bot] 413e9fd40e
chore(deps): bump urllib3 in /PROJECTS/beginner/keylogger
Bumps [urllib3](https://github.com/urllib3/urllib3) from 2.6.3 to 2.7.0.
- [Release notes](https://github.com/urllib3/urllib3/releases)
- [Changelog](https://github.com/urllib3/urllib3/blob/main/CHANGES.rst)
- [Commits](https://github.com/urllib3/urllib3/compare/2.6.3...2.7.0)

---
updated-dependencies:
- dependency-name: urllib3
  dependency-version: 2.7.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-05-12 17:31:33 +00:00
CarterPerez-dev a46fa4465e fix(canary-phase3): address audit findings before rollup
Phase 3 audits returned one BLOCKER and one SPEC-VIOLATION plus two
should-fix items; all cleared in-phase per the no-rot rule.

BLOCKER — template.html double-escaped JS-context interpolations.
Go's html/template auto-escapes {{.X}} in <script> string-literal
position via its built-in jsstrescaper. Piping through the explicit
`| js` (JSEscaper) re-ran the escaper, producing `\\u003D` /
`\\u0026` on the wire (double backslash). JS parses `\\u003D` as a
literal backslash followed by "u003D" text rather than `=`, so any
realistic destination URL — e.g.
https://news.example.com/article?utm_source=newsletter&utm_medium=email
— was corrupted into
https://news.example.com/article?utm_source=newsletter&utm_medium=email
before being passed to window.location.replace. The unit tests
previously missed it because every test destination was an
escape-free string. Fix: drop `| js` from both JS-context actions
in template.html; the contextual auto-escaper alone produces single-
backslash `=` which JS correctly decodes. Spec §9.2 line 1031
and 1038 (and the explanatory paragraph 1045) are factually wrong
about `| js` being a custom template func — it is `JSEscaper`, and
adding it on top of the built-in contextual JS string escaper is
pure double-escape harm. Spec docs (local-only, not committed)
amended accordingly. Added
TestTrigger_DestinationRoundtripsThroughJSStringDecode as a
regression guard against re-introducing the pipeline.

SPEC-VIOLATION — nil-token Trigger returned `404 + "Not Found"`,
breaking spec §8.5 ("token not found | 200, ... deliberately do NOT
404") and §8.5 line 964 ("/c/* endpoints never return JSON errors").
A 404 on a non-existent token ID lets scanners enumerate which IDs
are real. Fix: nil-token branch now renders the same template with a
benign decoy destination ("/") and returns 200 + text/html with the
full CSP override and cache headers. Response is byte-shape
indistinguishable from a valid-token response except for the
destination URL embedded in the script. Added
TestTrigger_TokenNotFound_HasSameResponseShapeAsValidToken to
assert the indistinguishability invariant. The `(nil event, &resp,
nil error)` return shape from the handoff anti-relitigation is
preserved.

SHOULD-FIX S1 — extractDestination now allowlists `http://` and
`https://` (case-insensitive) and returns the new
ErrInvalidDestinationScheme sentinel otherwise. The noscript
meta-refresh sits in an HTML-attribute context where html/template
does NOT recognize `url=...` as a URL context, so `javascript:`,
`data:`, `file:`, `vbscript:` and scheme-relative `//example.com`
would otherwise render verbatim and follow on JS-disabled clients.
Phase 9's `validate:"url"` on input is necessary but not sufficient
(the validator's `url` tag accepts any scheme). Added
TestGenerate_RejectsDangerousDestinationSchemes (8 cases) and
TestGenerate_AcceptsHTTPAndHTTPSSchemes (4 case-variants).

SHOULD-FIX S3 — fingerprint handler now silently 204s on any
Content-Type that does not start with "application/json". The
embedded template is the only legitimate caller and always sends
JSON; this rejects adversarial probes before the MaxBytesReader
read. Added
TestFingerprintHandler_WrongContentType_Returns204AndDoesNotTouchEvent.

SHOULD-FIX S4 — added integration tests for the oversize-body cap
(128 KiB body returns 204, Extra untouched) and the empty-token-id
guard (`/c//fingerprint`); the latter accepts chi's natural routing
behavior alongside the in-handler 204.

Non-functional cleanups: extracted shared template-render path
into renderWith / renderResponse / renderDecoyResponse to keep the
nil-token + happy-path bodies definitionally identical;
ErrMissingDestination and ErrInvalidDestinationScheme exported so
callers (and tests) can ErrorIs them; TestGenerate_RejectsMissing*
tightened to use require.ErrorIs(tc.wantErr) instead of bare
require.Error.

Pre-rollup gate (go build / vet / test -race unit+integration /
golangci-lint) is clean.
2026-05-12 06:46:45 -04:00
CarterPerez-dev be13d2f1c4 feat(canary): register slowredirect generator in registry
Phase 3 wiring: registry.Build now maps token.TypeSlowRedirect to
slowredirect.New() alongside the existing webbug entry. The pending-
types regression guard sheds SlowRedirect from its list (5 remain:
docx, pdf, kubeconfig, envfile, mysql) and the cardinality assertion
moves from "exactly one generator" to "exactly two".
2026-05-12 06:34:12 -04:00
CarterPerez-dev 6dd520f966 feat(canary): slowredirect fingerprint endpoint
POST /c/{id}/fingerprint handler that decodes a JSON fingerprint body
and merges it into the most recent event's Extra JSONB for the same
(token_id, source_ip) tuple within a 30s window via the existing
event.Repository.AttachFingerprint method. Decoupled from the concrete
repository through a small FingerprintAttacher interface so future
event.Service can swap in without touching this package.

Per spec §9.2 the fingerprint POST is enrichment-only: a missing match
(event.ErrNotFound), an invalid JSON body, an empty body, and a body
exceeding 64 KiB all silently return 204. Only unexpected repository
errors are logged via slog. The token id is read from chi.URLParam("id")
so the route mounts naturally under the existing trigger router in
Phase 9.

Three integration tests against testcontainers cover the happy-path
JSONB merge, the no-matching-event path (silent 204), and the
invalid-JSON path (204 plus stored Extra left untouched).
2026-05-12 06:34:04 -04:00
CarterPerez-dev 71823f5de2 feat(canary): slowredirect generator (Generate + Trigger + template)
HTML+JS browser-fingerprint redirect page rendered via html/template:
Generate reads metadata.destination_url off the token row and returns
KindURL with the trigger URL + persisted destination; Trigger renders
the embedded template (noscript meta-refresh + inline fetch posting
the fingerprint, then window.location.replace), returns the page with
Content-Security-Policy override allowing inline script + connect-src
'self', and emits the event capturing token id / source IP / UA /
Referer.

Nil-token Trigger returns nil event (FK guard) plus a 404 HTML body,
matching the established defensive-rendering pattern from webbug.
Destination_url is extracted defensively (missing/empty/whitespace
all surface ErrMissingDestination). realIP precedence copied from
webbug pending the Phase 9 middleware promotion.

19 unit-test assertions cover Type, Generate's URL+destination output,
five missing-destination edge cases, XSS neutralization in two
injection contexts (attribute escape + script-closing escape), CSP
override + cache headers, event-recording metadata, nil-token contract,
and per-call response independence.
2026-05-12 06:33:52 -04:00
CarterPerez-dev 1a240952c7 fix(canary-phase2): address audit findings before rollup
Two audit agents (code-reviewer + spec-adherence) both returned PASS but
flagged substantive findings. Per fix-in-phase / no-backlog-rot, clearing
every MEDIUM + LOW in-phase.

Findings addressed:

MEDIUM — realIP RemoteAddr fallback returned "IP:port" verbatim, where
the spec wants the bare IP (geoip + downstream parsing assume host-only).
Now uses net.SplitHostPort with raw-string fallback if not host:port.
Adds 4 RemoteAddr cases: IPv4 strips port, IPv6 bracket form strips
brackets+port, loopback IPv6, and the port-less raw fallback.

MEDIUM — realIP XFF branch accepted whatever rightmost-comma-split
produced, so headers like "198.51.100.1, " (trailing comma) yielded
"" and skipped XRI/RemoteAddr entirely. Extracted into lastNonEmptyXFF
which walks right-to-left and falls through cleanly. Adds two cases:
trailing-comma falls through, all-empty entries fall through.

MEDIUM — pixel.TransparentGIF was an exported mutable []byte; any caller
could clobber it process-wide (mutating one response's body would affect
every subsequent webbug trigger). Renamed to unexported transparentGIF
+ exposed pixel.Clone() and pixel.Len(). Webbug now calls pixel.Clone()
per trigger; new test TestTrigger_ResponseBodyIsIndependentCopyPerCall
verifies two triggers return independent slices.

LOW (reviewer-escalated to HIGH-for-Phase-3) — Trigger on nil token
returned a non-nil event with empty TokenID. Since events.token_id is a
NOT NULL FK to tokens.id, the Phase 3 handler could not have persisted
that event anyway, and the contract was implicit (would have required
an inline comment, which the no-comments rule forbids). Now: nil token
in → nil event, non-nil response out. Contract is explicit in the
return shape. Test asserts evt is nil for nil-token path.

LOW — no IPv6 coverage in IP-precedence tests. Added IPv6 cases for
XFF rightmost and three RemoteAddr forms (bracketed, loopback, no-port).

NIT — gracefulShutdown swallowed every shutdown error, returning nil
unconditionally. Now collects with errors.Join so callers can detect
partial-shutdown for telemetry/exit-code purposes.

Audits accepted as-is (NIT, not blocking):
- Artifact discriminated-union shape (Phase 3+ concern when other Kinds
  are produced)
- Registry returning bare map (read-only post-Build; concurrent reads
  are safe by Go's memory model)
- Build(_ Config) ignoring its arg (signature reserved for future
  stateful generators)

Quality verified post-fix: build/vet/lint clean (0 issues),
14 webbug tests + 6 pixel tests + 4 registry tests PASS under -race,
integration tests unchanged.
2026-05-12 02:56:29 -04:00
CarterPerez-dev c39b56b9af fix(canary): clear pre-audit lint debt + migrate golangci config to v2
Pre-audit gate (`golangci-lint run`) at the start of Phase 2 surfaced 15
issues that should have been zeroed before Phase 1 rollup. Per the
fix-in-phase rule (no backlog rot), clearing everything before Phase 2
audit agents run on a green tree.

Config:
- .golangci.yml: migrate `issues.exclude-rules` and `issues.exclude-dirs`
  to v2 syntax (`linters.exclusions.{rules,paths}`); test-file funlen/
  dupl/goconst exclusion now actually applies under golangci-lint v2.10
- .golangci.yml: add G706 to gosec excludes — false-positive log-injection
  reports on slog structured-logging call sites (slog separates message
  from kv args, immune to log-line injection by construction)

errcheck (5):
- cmd/canary/main.go: `_ = telemetry.Shutdown(...)` → log on error
- internal/token/repository.go: `defer stmt.Close()` → log on close error
- internal/event/repository.go: same as token
- internal/testutil/postgres.go: `_ = pgContainer.Terminate(...)` and
  `_ = db.Close()` → t.Logf on cleanup error (use distinct err names to
  avoid govet shadow)

funlen (1):
- cmd/canary/main.go: split `run` into `run` + `initTelemetry` +
  `mountRouter` + `gracefulShutdown` (was 55 statements, now under
  the 50 cap; helpers are individually well under)

govet shadow (1):
- cmd/canary/main.go: migrations check switched from `if err :=` to
  `if err =` (reuses outer err whose value was already consumed) — the
  inner short-decl was lexically shadowing without intent
- cmd/canary/main.go: select-case `err` renamed to `startErr` to avoid
  the same shadow pattern

golines (6, all auto-fixed by `golangci-lint run --fix`):
- main.go, config.go, telemetry.go, event/repository.go, token/dto.go,
  token/repository.go — long lines wrapped to 80-col

Verified post-fix: build OK, vet OK, unit tests PASS under -race,
integration tests PASS under -tags=integration -race (12s testcontainers),
golangci-lint reports 0 issues.
2026-05-12 02:49:56 -04:00
CarterPerez-dev bb95f74579 feat(canary): generator registry (token.Type → Generator)
Lives in its own subpackage to break the otherwise-cyclic import
(generators interface → webbug impl → generators interface): the registry
must import concrete implementations, which themselves import the
generators package for Artifact/TriggerResponse types.

- registry.Config { BaseURL string } (reserved for future stateful generators)
- registry.Registry = map[token.Type]generators.Generator
- registry.Build registers webbug only; subsequent phases append
  slowredirect, docx, pdf, kubeconfig, envfile, mysql

Tests assert webbug present, unknown type returns nil, pending types
are not yet claimed, Phase 2 cardinality is exactly 1.
2026-05-12 02:43:53 -04:00
CarterPerez-dev 9c0ad74973 feat(canary): webbug generator (Generate + Trigger)
- Generate returns KindURL artifact: baseURL/c/{id} (trailing slash trimmed)
- Trigger builds event from CF-Connecting-IP|XFF(rightmost)|XRI|RemoteAddr precedence,
  captures UA + Referer (nil when absent), returns 43-byte GIF response with
  no-store cache headers
- Nil-token path still returns GIF (spec §8.5 defense-in-depth)
- Tests: artifact shape, request metadata capture, IP precedence chain (5 cases),
  nil-pointer mapping for absent headers, GIF response shape + cache headers,
  missing-token path

realIP() lives here temporarily; promotes to middleware in Phase 9.
2026-05-12 02:42:17 -04:00
CarterPerez-dev 0b6e586eb1 feat(canary): shared 43-byte transparent GIF for image triggers
Used by webbug, docx, pdf, envfile triggers. Single source of truth.
Test asserts length, magic bytes, GIF89a trailer, and decodes via image/gif.
2026-05-12 02:40:24 -04:00
CarterPerez-dev 6cc724c19f feat(canary): Generator interface for token-type plugins
- ArtifactKind enum: url, file, text, connection_string
- Artifact struct discriminated by Kind
- TriggerResponse value-typed (handler writes; generator stays pure)
- Generator interface: Type, Generate, Trigger
2026-05-12 02:40:00 -04:00
dependabot[bot] cd1dac6240
chore(deps): bump urllib3 in /PROJECTS/beginner/c2-beacon/backend
Bumps [urllib3](https://github.com/urllib3/urllib3) from 2.6.3 to 2.7.0.
- [Release notes](https://github.com/urllib3/urllib3/releases)
- [Changelog](https://github.com/urllib3/urllib3/blob/main/CHANGES.rst)
- [Commits](https://github.com/urllib3/urllib3/compare/2.6.3...2.7.0)

---
updated-dependencies:
- dependency-name: urllib3
  dependency-version: 2.7.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-05-11 16:55:52 +00:00
CarterPerez-dev 697f4909d7 fix(canary-phase1): clear all post-phase-1 audit observations + header normalization
No 'logged for later' — every non-blocking finding from the Phase 1 audits
is fixed now, in this commit, before declaring the phase truly closed.

Code cleanups (5 items):
1. Database.SQLDB() *sql.DB accessor on core.Database; main.go uses
   db.SQLDB() instead of the awkward db.DB.DB triple-chain.
2. Hoisted list-default literals (50, 20) to package-level
   defaultListLimit consts in token + event repositories. MEMORY.md
   'no magic numbers' rule honored.
3. Moved ptr[T any] helper to internal/testutil/ptr.go as testutil.Ptr;
   removed local copies from token + event repository_test.go.
4. Renamed CHECK constraints in 0001_create_tokens.sql to match spec
   text exactly: chk_token_type → chk_type, chk_alert_channel → chk_channel.
   Spec was the original contract; impl now aligns.
5. Resolved APP_ENVIRONMENT vs ENVIRONMENT divergence per spec §12.1:
   - compose.yml: ENVIRONMENT=production → APP_ENVIRONMENT=production
   - dev.compose.yml: ENVIRONMENT=development → APP_ENVIRONMENT=development
   - config.go envKeyMap: ENVIRONMENT → APP_ENVIRONMENT mapping

Concurrency bug fix (real, not just polish):
- core/migrations.go: added sync.Mutex around goose calls. goose's
  package-level state (SetBaseFS, SetDialect) raced under t.Parallel()
  testcontainers tests. Race detector caught it under -race after
  parallel test counts climbed. Mutex serializes goose invocation
  globally; testcontainer-parallelism otherwise unaffected.

Header normalization (50 files):
- Bulk-normalized every project file's header to canonical
  '©AngelaMos | 2026' (no space, with © glyph, year 2026) per MEMORY.md
  style rule. Eliminated 4 distinct non-canonical variants: '// AngelaMos',
  '// ©AngelaMos | 2025', '// © AngelaMos | 202X', '# AngelaMos'.
- Verified clean: grep returns zero non-canonical headers across the
  whole project tree (excluding gitignored docs/ and node_modules/).

Backlog discipline:
- Created docs/plans/BACKLOG.md with strict format: open items only,
  HIGH/MEDIUM/LOW severity, must-clear-before-ship contract.
- BACKLOG currently has zero open items — every observation was fixed
  here, not deferred. Closed-items section logs what was cleared.

Verification:
- go build ./...                                     clean
- go vet ./...                                       clean
- go test -tags=integration -race ./internal/token/...  11/11 PASS
- go test -tags=integration -race ./internal/event/...   9/9 PASS
- docker compose -f compose.yml config              parses
- docker compose -f dev.compose.yml config          parses
- grep for non-canonical headers                     zero matches
2026-05-10 06:15:26 -04:00
CarterPerez-dev b2558e2e41 feat(canary): Phase 1 — Postgres schema + token & event repositories
Schema (3 goose migrations under internal/core/migrations/):
- 0001_create_tokens.sql: tokens table per spec §7.1
  Columns: id (varchar 12, PK), manage_id (UUID unique), type, memo,
           filename, alert_channel, telegram_bot/chat, webhook_url,
           created_at/ip/fp, enabled, trigger_count, last_triggered, metadata (jsonb)
  CHECK constraints: chk_token_type (7-token enum), chk_alert_channel (telegram|webhook),
                     chk_telegram_complete (when channel=telegram, bot+chat required),
                     chk_webhook_complete (when channel=webhook, url required)

- 0002_create_events.sql: events table
  Columns: id (bigserial PK), token_id (FK with ON DELETE CASCADE),
           triggered_at, source_ip (inet), user_agent, referer,
           geo_country/region/city/asn/asn_org, extra (jsonb),
           notify_status, notified_at
  CHECK chk_notify_status (pending|sent|failed|deduped)

- 0003_indexes.sql: full index set per spec §7.1
  tokens(created_ip), tokens(created_fp), tokens(created_at DESC),
  tokens(type), partial idx_tokens_trigger_count WHERE trigger_count > 0,
  events(token_id, triggered_at DESC), events(source_ip),
  partial idx_events_notify_pending WHERE notify_status = 'pending'

Migration runner (internal/core/migrations.go):
- go:embed migrations/*.sql baked into binary
- core.RunMigrations(*sql.DB) called from main.go after DB connect
- pressly/goose v3.27.1 (research-recommended; no dirty-state bug)

Token domain (internal/token/):
- entity.go: Token struct + typed Type/AlertChannel enums with Valid() guards
- dto.go: CreateRequest with validator/v10 tags (oneof, required_if, url, max),
          Response shape with ToResponse(triggerURL, manageURL) helper
- repository.go: Insert (RETURNING created_at + counters), GetByID,
                 GetByManageID, DeleteByManageID (FK cascade), IncrementTriggerCount,
                 SetEnabled, ListAll + CountAll. ErrNotFound on sql.ErrNoRows.
                 Named-parameter binding via sqlx.NamedExecContext.
- repository_test.go (//go:build integration): 11 tests covering insert,
  get-by-id, get-by-manage-id, not-found paths, delete with cascade,
  trigger count increment, enable toggle, list pagination, type CHECK

Event domain (internal/event/):
- entity.go: Event struct + NotifyStatus enum
- dto.go: GeoView + Response with ToResponse() flattens geo into nested object
- repository.go: Insert, GetByID, ListByToken (cursor pagination via
                 LIMIT N+1, returns NextCursor + HasMore), CountByToken,
                 AttachFingerprint (UPDATE most-recent within window using
                 jsonb || merge), UpdateNotifyStatus, PruneToLimit
                 (window function row_number() PARTITION BY token_id)
- repository_test.go (//go:build integration): 9 tests covering insert,
  cursor-paginated listing, FK cascade from token deletion, fingerprint
  merge into existing extra jsonb, notify status update, prune-to-N
  preserves newest-first, prune rejects zero limit

Test infrastructure:
- internal/testutil/postgres.go: testcontainers-go helper that spins up
  postgres:18-alpine, applies migrations via core.RunMigrations, returns
  ready *sql.DB with t.Cleanup-registered teardown. ~5-7s per test;
  20 integration tests run in ~13s end-to-end.

Wired into main.go:
- core.RunMigrations(db.DB.DB) called after NewDatabase
- token + event repos instantiated; blank-assigned for now (services
  consume them in Phase 9/10)

Verification:
- go build ./...                                      clean
- go vet ./...                                        clean
- go test -tags=integration ./internal/token/...      11 of 11 PASS
- go test -tags=integration ./internal/event/...      9 of 9 PASS
2026-05-10 05:47:24 -04:00
CarterPerez-dev 98c006897d fix(canary-phase0): address audit findings before phase rollup
Phase 0 audit (superpowers:code-reviewer + general-purpose) returned FAIL
on shared findings. This commit resolves the blockers + important items.

Blockers:
- infra/docker/vite.prod: replace 'react-scss/' COPY paths with 'frontend/'
  (would have broken 'docker compose build nginx')
- Delete backend/Dockerfile (broken template debris referencing deleted
  cmd/api, missing migrations/, deleted keys/)
- Delete backend/Justfile (docker compose recipes pointed at deleted
  backend/compose.yml + backend/dev.compose.yml; project-root justfile
  [backend] group covers everything operationally)
- backend/internal/admin/handler.go: strip the residual JWT/auth scaffolding
  (AuthService interface, authSvc field, authenticator+adminOnly mw params).
  Phase 12 will gate /admin under OperatorBearer; for now RegisterRoutes
  takes only chi.Router.
- Delete backend/.env + backend/.env.example: stale template duplicates
  containing only template keys; project-root .env.example is the source
  of truth per spec §12.4.

Important:
- Add © glyph to file headers in cloudflared.compose.yml, infra/docker/
  vite.{dev,prod}, backend/.gitignore (operator's standing rule:
  '©AngelaMos | 2026', not 'AngelaMos | 2026')
- backend/.gitignore: drop stale 'keys/*.pem' / 'keys/*.key' entries
  (backend/keys/ no longer exists)
- scripts/init.sh: rename 'local_tmp' variable to 'tmp_dir' to remove
  visual ambiguity with the 'local' bash keyword (both audit agents
  briefly misread it as 'local local_tmp=...')
- .env.example: add inline comment explaining the dual NGINX_HOST_PORT
  defaults (22784 prod compose, 58495 dev compose; do not override in
  shared .env)

Deferred (logged but not blocking Phase 0):
- Pre-existing template Go files (internal/{config,core,server,health,
  middleware/{request_id,logging,headers,ratelimit},admin}/*.go) carry
  '// AngelaMos | 2026' without ©. Bulk-fixing these belongs to a
  hygiene pass; not Phase 0 scope.
- frontend/vite.config.ts and frontend/stylelint.config.js reference
  '2025'; operator-imported template files, separate concern.
- APP_ENVIRONMENT vs ENVIRONMENT spec/impl mismatch: spec §12.1 sample
  uses APP_ENVIRONMENT; envKeyMap in config.go uses ENVIRONMENT (template
  default). Implementation is internally consistent. Logged in plan
  Appendix C as a non-blocking spec amendment candidate.

Verification: go build ./... + go vet ./... clean. grep returns no
hits for react-scss / carterperez-dev/templates / JWTConfig /
InvalidateAllSessions across backend/, infra/, scripts/, compose files.
2026-05-10 05:37:32 -04:00
CarterPerez-dev a4811d1c04 feat(canary): import operator's React + Vite frontend template
Frontend stack (pre-rebranded by operator before Phase 0):
- React 19 + react-router-dom v7
- TypeScript 5.9 with project references
- Vite 7 (rolldown-vite — Rust port)
- TanStack Query v5 + axios for data
- Zustand v5 for client state
- Zod v4 for runtime validation
- SCSS (Sass), not Tailwind — operator stylistic choice
- Biome 2 (lint + format), Stylelint for SCSS
- react-error-boundary, sonner (toasts)
- react-icons

Source layout (operator-authored skeleton):
- src/App.tsx, main.tsx, config.ts, styles.scss
- src/api/{hooks,types,index.ts}        TanStack Query hooks
- src/components/                        UI primitives (operator-designed later)
- src/core/{api,app,lib}                 axios client, routers, utilities
- src/pages/                             page-level containers
- src/styles/                            SCSS partials

Per implementation plan §16.3, Phase 14 stops at API client only —
operator owns visual/component design from there.

Pre-commit hooks fixed:
- site.webmanifest: missing trailing newline
- index.html: trailing whitespace stripped
- .stylelintignore + stylelint.config.js: chmod -x (no shebang, not executable)

node_modules/ is gitignored.
2026-05-10 05:28:09 -04:00
CarterPerez-dev a2d6f09ab3 feat(canary): add project-root .env.example + idempotent init.sh
.env.example covers every env var the canary stack reads:
- Public: APP_NAME, NGINX_HOST_PORT, PUBLIC_BASE_URL, VITE_APP_TITLE, VITE_API_URL
- Anti-bot: TURNSTILE_SITE_KEY/SECRET + VITE_TURNSTILE_SITE_KEY (frontend mirror)
- Operator: OPERATOR_TOKEN (admin endpoints)
- DB: POSTGRES_PASSWORD + POSTGRES_DEV_PORT
- Cache: REDIS_DEV_PORT
- GeoIP: MAXMIND_ACCOUNT_ID/LICENSE_KEY (optional)
- Webhooks: WEBHOOK_HMAC_SECRET (optional)
- Fake MySQL: MYSQL_FAKE_ENABLED + MYSQL_HOST_PORT (optional, off by default)
- Logging: LOG_LEVEL, LOG_FORMAT
- Telemetry: OTEL_ENABLED, OTEL_EXPORTER_OTLP_ENDPOINT + Jaeger ports
- Tunnel: CLOUDFLARE_TUNNEL_TOKEN (for cloudflared.compose.yml)

scripts/init.sh rewritten as idempotent setup helper:
- Copies .env.example -> .env on first run
- Generates POSTGRES_PASSWORD + OPERATOR_TOKEN via openssl rand -hex 32 if blank
- Skips already-set values (idempotent)
- Conditionally fetches GeoLite2-City.mmdb when MAXMIND creds present
- All sed usage is operator-side (script run-time), not Claude tool-time

scripts/randomize-ports.sh kept as-is (operator helper for spinning up
sibling projects on non-conflicting ports).
2026-05-10 05:27:30 -04:00
CarterPerez-dev 369c954892 feat(canary): unified Justfile with frontend + backend + compose groups
Replaces template Justfile (which referenced ./react-scss/). Groups:

[frontend]   pnpm install/dev/build/preview, biome, stylelint, tsc
             Targets at fe-install/fe-dev/fe-build/fe-preview prefixed for clarity
[backend]    go mod tidy, vet, golangci-lint, test (unit + integration tags),
             coverage HTML, run, static build, air hot reload
[lint]       lint = be-lint + biome + stylelint + tsc;  ci = lint + test
[compose]    docker compose lifecycle for prod (up/start/down/stop/build/logs/ps)
[tunnel]     overlay with cloudflared.compose.yml for prod+tunnel
[dev]        docker compose lifecycle for dev.compose.yml
[util]       init (gen secrets + fetch GeoLite2), ports, info, clean

Justfile parses cleanly via 'just --list'. Frontend dir reference fixed
(now ./frontend, not ./react-scss).
2026-05-10 05:25:27 -04:00
CarterPerez-dev a5c8d17134 feat(canary): add canary.dev + canary.prod Dockerfiles + import infra/
Production Dockerfile (infra/docker/canary.prod):
- Multi-stage: golang:1.25-alpine builder → distroless/static:nonroot final
- CGO_ENABLED=0, -trimpath, -ldflags='-s -w' → minimal static binary
- Embeds config.yaml; runs as nonroot user
- ENTRYPOINT /canary, CMD -config /config.yaml
- EXPOSE 8080

Development Dockerfile (infra/docker/canary.dev):
- golang:1.25-alpine + air-verse/air for hot reload
- Source bind-mounted by dev.compose.yml; .air.toml drives rebuilds
- go mod download cached at image-build time, dep refresh at runtime if changed
- EXPOSE 8080

Also imports the rest of the template's infra/ that was untracked:
- infra/docker/vite.dev + vite.prod (existing, unchanged)
- infra/nginx/{nginx.conf, dev.nginx, prod.nginx, nginx.prod.conf}
  These will be extended in Phase 15 with /api, /c, /k upstream blocks.
2026-05-10 05:24:42 -04:00
CarterPerez-dev 8b70bfbba9 chore(canary): merge backend compose into project root
Single project-root compose.yml + dev.compose.yml manage all services.

Production stack (compose.yml):
- nginx          public ingress on \${NGINX_HOST_PORT:-22784}:80
- canary         Go backend, NO host port (reachable only via nginx)
                 distroless/static:nonroot Dockerfile
                 healthcheck → /healthz, depends on postgres + redis
- postgres       postgres:18-alpine, no host port, named volume pgdata
- redis          redis:7-alpine, no host port, named volume redisdata
- (geolite vol)  read-only mount /data for GeoLite2-City.mmdb

Dev stack (dev.compose.yml):
- nginx          dev ingress on \${NGINX_HOST_PORT:-58495}:80
- frontend       Vite HMR on \${FRONTEND_HOST_PORT:-15723}:5173
- canary         Air hot-reload (canary.dev Dockerfile, bind-mounts ./backend)
                 OTel exports to jaeger:4317
- postgres       host port \${POSTGRES_DEV_PORT:-5447}:5432 for psql access
- redis          host port \${REDIS_DEV_PORT:-6022}:6379 for redis-cli access
- jaeger         UI 16686, OTLP gRPC 4317, OTLP HTTP 4318
- volumes        gocache + gomodcache speed up rebuilds

All randomized host ports preserved exactly:
  22784 (prod nginx), 58495 (dev nginx), 15723 (vite), 5447 (pg dev),
  6022 (redis dev), 16686/4317/4318 (jaeger).

Backend has no host port in production: nginx is the single entrypoint.
Cloudflare Tunnel sidecar terminates at nginx:80 via cloudflared.compose.yml.

backend/compose.yml + backend/dev.compose.yml deleted (merged here).

Validation:
- docker compose -f compose.yml config           — OK
- docker compose -f dev.compose.yml config       — OK
- docker compose -f compose.yml -f cloudflared.compose.yml config — OK
2026-05-10 05:23:55 -04:00
CarterPerez-dev 7fa2861e7a feat(canary): backend bootstrap — strip JWT/users, rename module, rewrite main
Removes the template's JWT auth + user domain (Phase 0 §0.4):
- Deleted backend/internal/auth/ (entire JWT auth domain)
- Deleted backend/internal/user/ (user CRUD domain)
- Deleted backend/keys/ (JWT signing keys directory)
- Deleted backend/internal/middleware/auth.go (Authenticator + RequireAdmin)
- Deleted Justfile generate-keys recipe (no JWT keys to generate)
- Removed lestrrat-go/jwx/v3 + transitive deps via go mod tidy
- Stripped JWTConfig type, defaults, env mappings, validators from config.go
- Removed jwt: section from config.yaml
- Removed JWT_* lines from backend/.env and .env.example

Renames Go module to project-local path (Phase 0 §0.5):
  github.com/carterperez-dev/templates/go-backend
  → github.com/CarterPerez-dev/cybersecurity-projects/canary-token-generator/backend
- Rewrote imports in 6 remaining .go files using Edit tool (NEVER sed per repo rule)
- Updated .golangci.yml local-prefixes + gci section ordering

Renames cmd/api → cmd/canary and rewrites main.go (Phase 0 §0.6):
- mv cmd/api cmd/canary
- Rewrote cmd/canary/main.go as canary bootstrap (config, telemetry, db,
  redis, middleware chain, health, /api stub) — no auth wiring
- Updated .air.toml cmd path
- Updated backend/Justfile run/build targets to cmd/canary + bin/canary
- Renamed docker-build image tag to canary-token-generator:latest

Rebrands defaults:
- config.go: app.name "Go Backend" → "Canary Token Generator"
- config.go: otel.service_name "go-backend" → "canary-token-generator"
- config.yaml: app.name "Go Backend Template" → "Canary Token Generator"
- backend/.env(.example): OTEL_SERVICE_NAME → canary-token-generator

Drops user-aware rate-limit helpers from middleware/ratelimit.go:
- Removed KeyByUser, KeyByUserAndEndpoint, normalizeEndpoint, isUUID, isNumeric
- Removed TierConfig, DefaultTiers, TieredRateLimiter (referenced GetUserID)
- KeyByIP, NewRateLimiter, PerMinute/PerSecond/PerHour preserved

Verification:
- go build ./...    — clean
- go vet ./...      — clean
- go mod tidy       — silent (deps consolidated)
- grep -r "carterperez-dev/templates|JWTConfig|JWT_PRIVATE|lestrrat" → empty

backend/compose.yml + backend/dev.compose.yml are still present here; they
get merged into project-root compose files in Task 0.7 (next commit).
2026-05-10 05:22:08 -04:00
CarterPerez-dev 382890cb56 chore(canary): rebrand from no-auth-template to canary-token-generator
- compose.yml: APP_NAME default → canary-token-generator
- dev.compose.yml: APP_NAME default → canary-token-generator
                   ./react-scss bind paths → ./frontend (template flatten)
- cloudflared.compose.yml: APP_NAME default → canary-token-generator
- VITE_APP_TITLE default in both compose files → "Canary Token Generator"

Frontend package.json and index.html were already rebranded by operator
between sessions. All three compose stacks now validate via docker compose
config (prod, dev, prod+tunnel overlay).
2026-05-10 05:15:09 -04:00
CarterPerez-dev 5ccabe00b5 chore(canary): expand project .gitignore for Go + Node + secrets
- Ignore docs/ (dev-only planning material)
- Ignore env files except .env.example
- Ignore backend Go build/test artifacts (bin, tmp, coverage)
- Ignore frontend Node + Vite caches and dist
- Ignore local data volumes (geoip mmdb, sqlite databases)
- Ignore OS/editor cruft and lint caches
2026-05-10 05:13:19 -04:00
dependabot[bot] 606034ee2c
chore(deps): bump python-multipart
Bumps [python-multipart](https://github.com/Kludex/python-multipart) from 0.0.26 to 0.0.27.
- [Release notes](https://github.com/Kludex/python-multipart/releases)
- [Changelog](https://github.com/Kludex/python-multipart/blob/main/CHANGELOG.md)
- [Commits](https://github.com/Kludex/python-multipart/compare/0.0.26...0.0.27)

---
updated-dependencies:
- dependency-name: python-multipart
  dependency-version: 0.0.27
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-05-08 12:15:41 +00:00
CarterPerez-dev 7d69339413 fix: extract osv package string literals to constants
goconst counts occurrences across the whole package (including test files)
when deciding whether to flag a non-test file — the exclusion only suppresses
reports FROM test files. Add unexported constants for severity levels, CVSS
types, ecosystem, and reference types in client.go, and update client_test.go
to use them so the total raw-string count per literal drops below threshold.
2026-05-08 06:30:08 -04:00
CarterPerez-dev 25671b4f9d fix: resolve simple-vulnerability-scanner goconst failures
Migrate .golangci.yml from v1 issues.exclude-rules to v2
linters.exclusions.rules — the v1 key was silently ignored by golangci-lint
v2, so test-file goconst exclusions weren't applied. With the config fixed,
all test-file violations disappear. Extract severity constants in output.go
to fix the 4 remaining non-test goconst violations (CRITICAL/HIGH/MODERATE/
LOW each appear 3x in severityRank, severityBreakdown, and severityColorFn).
2026-05-08 06:21:26 -04:00
CarterPerez-dev 5863be7dac fix: pin pnpm to v10 in CI and add .npmrc to all frontend projects
pnpm latest on Node 22 resolves to pnpm 11 which rejects lockfileVersion
9.0 lockfiles with ERR_PNPM_LOCKFILE_CONFIG_MISMATCH. Pinning to pnpm 10
keeps compatibility with existing lockfiles. .npmrc sets
strict-dep-builds=false so build-script warnings don't hard-fail the
frozen install.
2026-05-08 05:46:38 -04:00
Carter Perez 2a4f559ea2
Update DEMO.md 2026-04-28 18:03:08 -04:00
Carter Perez 0fbd09c264
Update DEMO.md 2026-04-28 18:01:21 -04:00
CarterPerez-dev b70da08b70 Merge remote-tracking branch 'origin/main' into chore/haskell-reverse-proxy-finish
# Conflicts:
#	TEMPLATES/fullstack-template
2026-04-28 17:57:03 -04:00
Carter Perez d40293490e
Merge pull request #188 from CarterPerez-dev/dependabot/uv/PROJECTS/beginner/c2-beacon/backend/python-dotenv-1.2.2
chore(deps): bump python-dotenv from 1.2.1 to 1.2.2 in /PROJECTS/beginner/c2-beacon/backend
2026-04-26 23:51:57 -04:00
CarterPerez-dev ef315b072b chore: add demos for projects, update haskell-reverse-proxy modules, refresh siem assets
- Add DEMO.md and screenshots for bug-bounty-platform, hash-cracker,
  linux-cis-hardening-auditor, simple-port-scanner, simple-vulnerability-scanner,
  systemd-persistence-scanner, base64-tool, caesar-cipher, dns-lookup,
  metadata-scrubber-tool, network-traffic-analyzer, siem-dashboard
- Link DEMO.md from project READMEs
- Add .gitignore entries for DEMO-TRACKER and simple-port-scanner build dir
- Restructure haskell-reverse-proxy with DDoS, Fingerprint, ML, RateLimit, WAF,
  Geo, and Honeypot modules; drop superseded research docs and old Makefile
- Refresh siem-dashboard dashboard.png and rename alerts.png to alert-detail.png
2026-04-26 23:12:48 -04:00
dependabot[bot] c122bcd914
chore(deps): bump lxml in /PROJECTS/beginner/metadata-scrubber-tool
Bumps [lxml](https://github.com/lxml/lxml) from 6.0.2 to 6.1.0.
- [Release notes](https://github.com/lxml/lxml/releases)
- [Changelog](https://github.com/lxml/lxml/blob/master/CHANGES.txt)
- [Commits](https://github.com/lxml/lxml/compare/lxml-6.0.2...lxml-6.1.0)

---
updated-dependencies:
- dependency-name: lxml
  dependency-version: 6.1.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-21 23:10:02 +00:00
dependabot[bot] 67e59f2255
chore(deps): bump python-dotenv in /PROJECTS/beginner/c2-beacon/backend
Bumps [python-dotenv](https://github.com/theskumar/python-dotenv) from 1.2.1 to 1.2.2.
- [Release notes](https://github.com/theskumar/python-dotenv/releases)
- [Changelog](https://github.com/theskumar/python-dotenv/blob/main/CHANGELOG.md)
- [Commits](https://github.com/theskumar/python-dotenv/compare/v1.2.1...v1.2.2)

---
updated-dependencies:
- dependency-name: python-dotenv
  dependency-version: 1.2.2
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-21 22:37:58 +00:00
dependabot[bot] 69a1a3989a
chore(deps): bump pypdf in /PROJECTS/beginner/metadata-scrubber-tool
Bumps [pypdf](https://github.com/py-pdf/pypdf) from 6.10.1 to 6.10.2.
- [Release notes](https://github.com/py-pdf/pypdf/releases)
- [Changelog](https://github.com/py-pdf/pypdf/blob/main/CHANGELOG.md)
- [Commits](https://github.com/py-pdf/pypdf/compare/6.10.1...6.10.2)

---
updated-dependencies:
- dependency-name: pypdf
  dependency-version: 6.10.2
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-16 22:32:38 +00:00
Carter Perez cac4a65232
Merge pull request #172 from CarterPerez-dev/dependabot/uv/PROJECTS/beginner/metadata-scrubber-tool/pypdf-6.10.1
chore(deps): bump pypdf from 6.10.0 to 6.10.1 in /PROJECTS/beginner/metadata-scrubber-tool
2026-04-15 19:40:51 -04:00
Carter Perez 87ed5c73e6
Merge pull request #174 from CarterPerez-dev/dependabot/uv/PROJECTS/beginner/c2-beacon/backend/python-multipart-0.0.26
chore(deps): bump python-multipart from 0.0.22 to 0.0.26 in /PROJECTS/beginner/c2-beacon/backend
2026-04-15 19:40:23 -04:00
Carter Perez cccd383b30
Merge branch 'main' into project/systemd-persistence-scanner 2026-04-15 19:37:43 -04:00
CarterPerez-dev 8c8a37c654 docs: add learn folder and update README for systemd persistence scanner 2026-04-15 19:21:56 -04:00
dependabot[bot] d3b48ef80a
chore(deps): bump python-multipart
Bumps [python-multipart](https://github.com/Kludex/python-multipart) from 0.0.22 to 0.0.26.
- [Release notes](https://github.com/Kludex/python-multipart/releases)
- [Changelog](https://github.com/Kludex/python-multipart/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Kludex/python-multipart/compare/0.0.22...0.0.26)

---
updated-dependencies:
- dependency-name: python-multipart
  dependency-version: 0.0.26
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-15 21:13:20 +00:00
dependabot[bot] 64fb1d0dbd
chore(deps): bump pypdf in /PROJECTS/beginner/metadata-scrubber-tool
Bumps [pypdf](https://github.com/py-pdf/pypdf) from 6.10.0 to 6.10.1.
- [Release notes](https://github.com/py-pdf/pypdf/releases)
- [Changelog](https://github.com/py-pdf/pypdf/blob/main/CHANGELOG.md)
- [Commits](https://github.com/py-pdf/pypdf/compare/6.10.0...6.10.1)

---
updated-dependencies:
- dependency-name: pypdf
  dependency-version: 6.10.1
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-15 20:42:36 +00:00
CarterPerez-dev aa0c03d0e9 feat: add systemd persistence scanner project
17 scanner modules covering systemd, cron, shell profiles, SSH,
XDG autostart, PAM, udev, kernel modules, LD_PRELOAD, MOTD,
init.d, at jobs, logrotate hooks, systemd generators, bash
completion, network interface hooks, and sshrc.

16 detection patterns with MITRE ATT&CK mappings, baseline
diffing, JSON output, and ignore-list support.

go install: github.com/CarterPerez-dev/sentinel/cmd/sentinel@latest
2026-04-15 09:00:16 -04:00
Carter Perez ec319b2e49
Merge pull request #164 from CarterPerez-dev/dependabot/uv/PROJECTS/beginner/keylogger/pytest-9.0.3
chore(deps): bump pytest from 8.4.1 to 9.0.3 in /PROJECTS/beginner/keylogger
2026-04-15 07:03:07 -04:00
Carter Perez 928d9ee68d
Merge pull request #163 from CarterPerez-dev/dependabot/uv/PROJECTS/beginner/network-traffic-analyzer/python/pillow-12.2.0
chore(deps): bump pillow from 12.1.1 to 12.2.0 in /PROJECTS/beginner/network-traffic-analyzer/python
2026-04-15 07:02:54 -04:00
Carter Perez cfdd501975
Merge pull request #161 from CarterPerez-dev/dependabot/uv/PROJECTS/beginner/metadata-scrubber-tool/pillow-12.2.0
chore(deps): bump pillow from 12.1.0 to 12.2.0 in /PROJECTS/beginner/metadata-scrubber-tool
2026-04-15 07:02:20 -04:00
dependabot[bot] 5b2b7e86a4
chore(deps): bump pytest in /PROJECTS/beginner/caesar-cipher
Bumps [pytest](https://github.com/pytest-dev/pytest) from 9.0.2 to 9.0.3.
- [Release notes](https://github.com/pytest-dev/pytest/releases)
- [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pytest-dev/pytest/compare/9.0.2...9.0.3)

---
updated-dependencies:
- dependency-name: pytest
  dependency-version: 9.0.3
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-15 11:01:27 +00:00
dependabot[bot] bb0ad647ae
chore(deps): bump pytest in /PROJECTS/beginner/keylogger
Bumps [pytest](https://github.com/pytest-dev/pytest) from 8.4.1 to 9.0.3.
- [Release notes](https://github.com/pytest-dev/pytest/releases)
- [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pytest-dev/pytest/compare/8.4.1...9.0.3)

---
updated-dependencies:
- dependency-name: pytest
  dependency-version: 9.0.3
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-15 11:01:10 +00:00
Carter Perez 4a161754de
Merge pull request #167 from CarterPerez-dev/dependabot/uv/PROJECTS/beginner/base64-tool/pytest-9.0.3
chore(deps): bump pytest from 9.0.2 to 9.0.3 in /PROJECTS/beginner/base64-tool
2026-04-15 07:01:01 -04:00
Carter Perez 703a0c7679
Merge pull request #168 from CarterPerez-dev/dependabot/uv/PROJECTS/beginner/dns-lookup/pytest-9.0.3
chore(deps): bump pytest from 9.0.2 to 9.0.3 in /PROJECTS/beginner/dns-lookup
2026-04-15 07:00:40 -04:00
dependabot[bot] 5eb71369e3
chore(deps): bump pytest
Bumps [pytest](https://github.com/pytest-dev/pytest) from 9.0.2 to 9.0.3.
- [Release notes](https://github.com/pytest-dev/pytest/releases)
- [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pytest-dev/pytest/compare/9.0.2...9.0.3)

---
updated-dependencies:
- dependency-name: pytest
  dependency-version: 9.0.3
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-14 03:06:39 +00:00
dependabot[bot] 00e0141b41
chore(deps): bump pytest in /PROJECTS/beginner/dns-lookup
Bumps [pytest](https://github.com/pytest-dev/pytest) from 9.0.2 to 9.0.3.
- [Release notes](https://github.com/pytest-dev/pytest/releases)
- [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pytest-dev/pytest/compare/9.0.2...9.0.3)

---
updated-dependencies:
- dependency-name: pytest
  dependency-version: 9.0.3
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-14 02:59:57 +00:00
dependabot[bot] 93849e855c
chore(deps): bump pytest in /PROJECTS/beginner/base64-tool
Bumps [pytest](https://github.com/pytest-dev/pytest) from 9.0.2 to 9.0.3.
- [Release notes](https://github.com/pytest-dev/pytest/releases)
- [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pytest-dev/pytest/compare/9.0.2...9.0.3)

---
updated-dependencies:
- dependency-name: pytest
  dependency-version: 9.0.3
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-14 02:37:32 +00:00
dependabot[bot] 07ad8379ee
chore(deps): bump pillow
Bumps [pillow](https://github.com/python-pillow/Pillow) from 12.1.1 to 12.2.0.
- [Release notes](https://github.com/python-pillow/Pillow/releases)
- [Changelog](https://github.com/python-pillow/Pillow/blob/main/CHANGES.rst)
- [Commits](https://github.com/python-pillow/Pillow/compare/12.1.1...12.2.0)

---
updated-dependencies:
- dependency-name: pillow
  dependency-version: 12.2.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-13 23:47:13 +00:00
dependabot[bot] bcf2ca34ae
chore(deps): bump pillow in /PROJECTS/beginner/metadata-scrubber-tool
Bumps [pillow](https://github.com/python-pillow/Pillow) from 12.1.0 to 12.2.0.
- [Release notes](https://github.com/python-pillow/Pillow/releases)
- [Changelog](https://github.com/python-pillow/Pillow/blob/main/CHANGES.rst)
- [Commits](https://github.com/python-pillow/Pillow/compare/12.1.0...12.2.0)

---
updated-dependencies:
- dependency-name: pillow
  dependency-version: 12.2.0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-13 23:37:05 +00:00
Carter Perez f7081e9dd4
Merge pull request #157 from CarterPerez-dev/dependabot/uv/PROJECTS/beginner/metadata-scrubber-tool/pypdf-6.10.0
chore(deps): bump pypdf from 6.9.2 to 6.10.0 in /PROJECTS/beginner/metadata-scrubber-tool
2026-04-11 05:58:32 -04:00
CarterPerez-dev 4c5bce05c1 add: AGPL 3.0 license files to hash-cracker, ebpf tracer, dlp scanner 2026-04-11 05:56:43 -04:00
CarterPerez-dev d3fa5ea132 fix: update READMEs for ebpf tracer & dlp scanner
Add ASCII art, project number badges, justfile tip, and learn module
links. Add missing justfile to dlp-scanner. Update main README with
source code links and bump project count to 23/67.
2026-04-11 05:38:49 -04:00
Carter Perez db3e25e886
Merge pull request #160 from CarterPerez-dev/project/linux-ebpf-security-tracer
feat: linux ebpf security tracer project
2026-04-11 05:09:18 -04:00
CarterPerez-dev d292f1e4b7 feat: linux ebpf security tracer project
eBPF-based security monitoring tool with process, file, network,
privilege escalation, and system call tracing via BCC. Includes
threat detection engine, Rich TUI renderer, and learn docs.
2026-04-11 04:54:57 -04:00
dependabot[bot] d6db8a6ea3
chore(deps): bump pypdf in /PROJECTS/beginner/metadata-scrubber-tool
Bumps [pypdf](https://github.com/py-pdf/pypdf) from 6.9.2 to 6.10.0.
- [Release notes](https://github.com/py-pdf/pypdf/releases)
- [Changelog](https://github.com/py-pdf/pypdf/blob/main/CHANGELOG.md)
- [Commits](https://github.com/py-pdf/pypdf/compare/6.9.2...6.10.0)

---
updated-dependencies:
- dependency-name: pypdf
  dependency-version: 6.10.0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-10 21:36:18 +00:00
Carter Perez 9edf1f6cff
Merge pull request #153 from CarterPerez-dev/project/sbom-generator-vulnerability-matcher
feat: sbom generator & vulnerability matcher + docstrings across 6 pr…
2026-04-08 23:54:50 -04:00
CarterPerez-dev d277d50a93 feat: sbom generator & vulnerability matcher + docstrings across 6 projects
Add bomber CLI tool (Go) — scans dependencies across Go/Node/Python
ecosystems, generates SPDX 2.3 and CycloneDX 1.5 SBOMs, and matches
against OSV/NVD vulnerability databases with policy engine for CI/CD.

Add file-level docstrings to ai-threat-detection, firewall-rule-engine,
hash-cracker, linux-cis-hardening-auditor, binary-analysis-tool, and
credential-enumeration.
2026-04-08 23:53:40 -04:00
Carter Perez d856257b3c
Update README.md 2026-04-08 00:46:42 -04:00
Carter Perez e96420176c
Merge branch 'main' into project/credential-harvester 2026-04-04 20:23:24 -04:00
CarterPerez-dev c1ec748dda feat: credential enumeration complete 2026-04-04 20:21:19 -04:00
Carter Perez 2e182b39d7
Update README.md 2026-03-30 16:03:21 -04:00
Carter Perez 9f01c0219b
Merge pull request #138 from CarterPerez-dev/project/linux-cis-hardening-auditor
Add linux CIS hardening auditor
2026-03-30 15:52:46 -04:00
CarterPerez-dev ec7a41bb3f project linux-cis-hardening-auditor complete 2026-03-30 15:50:06 -04:00
dependabot[bot] f037ff0a88
chore(deps): bump pygments in /PROJECTS/beginner/c2-beacon/backend
Bumps [pygments](https://github.com/pygments/pygments) from 2.19.2 to 2.20.0.
- [Release notes](https://github.com/pygments/pygments/releases)
- [Changelog](https://github.com/pygments/pygments/blob/master/CHANGES)
- [Commits](https://github.com/pygments/pygments/compare/2.19.2...2.20.0)

---
updated-dependencies:
- dependency-name: pygments
  dependency-version: 2.20.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-30 17:24:59 +00:00
Carter Perez 1dc475d9ef
Update project badge from #8 to #18 in README 2026-03-26 00:46:55 -04:00
Carter Perez c3e193fe4c
Update README.md 2026-03-26 00:46:26 -04:00
Carter Perez 854ceeb9da
Merge pull request #127 from CarterPerez-dev/project/firewall-rule-engine
feat: complete project firewall-rule-engine
2026-03-26 00:45:27 -04:00
CarterPerez-dev 22bb337320 feat: complete project firewall-rule-engine 2026-03-26 00:41:59 -04:00
Carter Perez 934a59c10f
Merge pull request #125 from CarterPerez-dev/dependabot/uv/PROJECTS/beginner/metadata-scrubber-tool/pypdf-6.9.2
chore(deps): bump pypdf from 6.9.1 to 6.9.2 in /PROJECTS/beginner/metadata-scrubber-tool
2026-03-25 18:47:27 -04:00
dependabot[bot] 7b9f9e49a3
chore(deps): bump requests in /PROJECTS/beginner/keylogger
Bumps [requests](https://github.com/psf/requests) from 2.32.5 to 2.33.0.
- [Release notes](https://github.com/psf/requests/releases)
- [Changelog](https://github.com/psf/requests/blob/main/HISTORY.md)
- [Commits](https://github.com/psf/requests/compare/v2.32.5...v2.33.0)

---
updated-dependencies:
- dependency-name: requests
  dependency-version: 2.33.0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-25 21:01:04 +00:00
dependabot[bot] a520b6014d
chore(deps): bump pypdf in /PROJECTS/beginner/metadata-scrubber-tool
Bumps [pypdf](https://github.com/py-pdf/pypdf) from 6.9.1 to 6.9.2.
- [Release notes](https://github.com/py-pdf/pypdf/releases)
- [Changelog](https://github.com/py-pdf/pypdf/blob/main/CHANGELOG.md)
- [Commits](https://github.com/py-pdf/pypdf/compare/6.9.1...6.9.2)

---
updated-dependencies:
- dependency-name: pypdf
  dependency-version: 6.9.2
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-25 20:47:17 +00:00
CarterPerez-dev e696b084a1 fix: suppress gosec G703 false positive on sanitized paths 2026-03-22 22:28:37 -04:00
Carter Perez 3bf0efcc07
Merge pull request #119 from CarterPerez-dev/project/hash-cracker
Project/hash cracker
2026-03-22 21:07:59 -04:00
CarterPerez-dev 9e314908b2 feat: complete project hash-cracker 2026-03-22 21:00:23 -04:00
CarterPerez-dev e76a7224ba fix: replace emojis with Unicode symbols, add wordlist and symlink install
Swap emoji icons for clean Unicode symbols (✓ ✗ ◆ ▸ ⏱) matching the
Go project style. Include 10k-most-common.txt wordlist for zero-friction
Quick Start. install.sh now symlinks binary to ~/.local/bin/hashcracker.
Fix DIM unbound variable in install.sh.
2026-03-22 11:24:28 -04:00
CarterPerez-dev efdfacdb55 feat: install.sh, Justfile, and README for zero-friction setup
install.sh detects package manager and installs all dependencies.
Justfile provides build, test, run, and clean commands.
README follows repo convention with ASCII banner and badges.
2026-03-22 10:46:14 -04:00
CarterPerez-dev f90a6ada5e feat: CLI with Boost.program_options and hash type dispatch
Full argument parsing for all attack modes, hash auto-detection,
salt support, thread count, and JSON output. Dispatch resolves
hasher + attack at compile time via template instantiation.
2026-03-22 10:39:07 -04:00
CarterPerez-dev 3e2c3ff002 feat: Engine, ThreadPool, and Progress display
ThreadPool: jthread-based work partitioning with SharedState atomics.
Progress: rich terminal output with Unicode box drawing, ANSI colors,
progress bar, speed/ETA display, and cracked/exhausted result rendering.
Engine: template function wiring hasher + attack + threading + salt.
3 integration tests passing (crack, exhaust, salted crack).
2026-03-22 10:29:06 -04:00
CarterPerez-dev 6542a097f8 feat: all attack strategies (dictionary, brute-force, rules)
DictionaryAttack: mmap-based wordlist reader with thread partitioning.
BruteForceAttack: keyspace generator with mathematical partitioning.
RuleSet: 7 mutation transforms via std::generator (capitalize, leet,
append/prepend digits, reverse, toggle case).
RuleAttack: combines dictionary words with rule mutations.
22 new tests, all passing.
2026-03-22 09:25:09 -04:00
CarterPerez-dev dd27eb2620 feat: all hashers (MD5, SHA1, SHA256, SHA512) and HashDetector
OpenSSL EVP-based hashers verified against NIST known-answer vectors.
HashDetector auto-identifies hash type from hex length with validation.
15 new tests, all passing.
2026-03-22 08:01:01 -04:00
CarterPerez-dev 4771d29342 feat: project scaffolding with CMake, configs, stubs, and core types
Includes CMakeLists.txt, CMakePresets.json, .clang-format, .clang-tidy,
Config.hpp (all constants), Concepts.hpp (Hasher/AttackStrategy concepts),
and stub implementations for all source files. All 6 stub tests pass.
2026-03-21 19:37:20 -04:00
dependabot[bot] 857f234bbe
chore(deps): bump pypdf in /PROJECTS/beginner/metadata-scrubber-tool
Bumps [pypdf](https://github.com/py-pdf/pypdf) from 6.8.0 to 6.9.1.
- [Release notes](https://github.com/py-pdf/pypdf/releases)
- [Changelog](https://github.com/py-pdf/pypdf/blob/main/CHANGELOG.md)
- [Commits](https://github.com/py-pdf/pypdf/compare/6.8.0...6.9.1)

---
updated-dependencies:
- dependency-name: pypdf
  dependency-version: 6.9.1
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-18 22:20:00 +00:00
dependabot[bot] 43de4eb145
chore(deps): bump pypdf in /PROJECTS/beginner/metadata-scrubber-tool
Bumps [pypdf](https://github.com/py-pdf/pypdf) from 6.7.5 to 6.8.0.
- [Release notes](https://github.com/py-pdf/pypdf/releases)
- [Changelog](https://github.com/py-pdf/pypdf/blob/main/CHANGELOG.md)
- [Commits](https://github.com/py-pdf/pypdf/compare/6.7.5...6.8.0)

---
updated-dependencies:
- dependency-name: pypdf
  dependency-version: 6.8.0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-11 00:54:25 +00:00
dependabot[bot] 9d7b1e23b3
chore(deps): bump pypdf in /PROJECTS/beginner/metadata-scrubber-tool
Bumps [pypdf](https://github.com/py-pdf/pypdf) from 6.7.4 to 6.7.5.
- [Release notes](https://github.com/py-pdf/pypdf/releases)
- [Changelog](https://github.com/py-pdf/pypdf/blob/main/CHANGELOG.md)
- [Commits](https://github.com/py-pdf/pypdf/compare/6.7.4...6.7.5)

---
updated-dependencies:
- dependency-name: pypdf
  dependency-version: 6.7.5
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-03 15:15:50 +00:00
dependabot[bot] 77041ba36f
chore(deps): bump pypdf in /PROJECTS/beginner/metadata-scrubber-tool
Bumps [pypdf](https://github.com/py-pdf/pypdf) from 6.7.3 to 6.7.4.
- [Release notes](https://github.com/py-pdf/pypdf/releases)
- [Changelog](https://github.com/py-pdf/pypdf/blob/main/CHANGELOG.md)
- [Commits](https://github.com/py-pdf/pypdf/compare/6.7.3...6.7.4)

---
updated-dependencies:
- dependency-name: pypdf
  dependency-version: 6.7.4
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-28 19:20:08 +00:00
Carter Perez 65e85fce39
Merge pull request #108 from CarterPerez-dev/project/ai-threat-detection
Project/ai threat detection phase 2-4
2026-02-28 14:19:00 -05:00
CarterPerez-dev f9b5a614d3 feat: did a couple things idk 2026-02-28 14:08:36 -05:00
dependabot[bot] 4edb98c147
Bump pypdf in /PROJECTS/beginner/metadata-scrubber-tool
Bumps [pypdf](https://github.com/py-pdf/pypdf) from 6.7.2 to 6.7.3.
- [Release notes](https://github.com/py-pdf/pypdf/releases)
- [Changelog](https://github.com/py-pdf/pypdf/blob/main/CHANGELOG.md)
- [Commits](https://github.com/py-pdf/pypdf/compare/6.7.2...6.7.3)

---
updated-dependencies:
- dependency-name: pypdf
  dependency-version: 6.7.3
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-27 17:45:52 +00:00
CarterPerez-dev 9397b12a4a add learn folder, move hidden files, update root readme, add learn folder, add clang tidy, and format 2026-02-26 00:38:55 -05:00
Carter Perez 6f26091240
Merge pull request #104 from CarterPerez-dev/dependabot/uv/PROJECTS/beginner/metadata-scrubber-tool/pypdf-6.7.2
Bump pypdf from 6.7.1 to 6.7.2 in /PROJECTS/beginner/metadata-scrubber-tool
2026-02-25 18:43:22 -05:00
deniskhud d324758c84 fix bugs, data races, add: install bash script, just cfg, CMakePresets 2026-02-25 19:50:20 +01:00
deniskhud 1ade9d6dc2 fix bugs, data races, add: install bash script, just cfg, CMakePresets 2026-02-25 19:40:52 +01:00
dependabot[bot] e02f18e20c
Bump pypdf in /PROJECTS/beginner/metadata-scrubber-tool
Bumps [pypdf](https://github.com/py-pdf/pypdf) from 6.7.1 to 6.7.2.
- [Release notes](https://github.com/py-pdf/pypdf/releases)
- [Changelog](https://github.com/py-pdf/pypdf/blob/main/CHANGELOG.md)
- [Commits](https://github.com/py-pdf/pypdf/compare/6.7.1...6.7.2)

---
updated-dependencies:
- dependency-name: pypdf
  dependency-version: 6.7.2
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-25 16:44:14 +00:00
Carter Perez bc6ee16629
Merge pull request #102 from CarterPerez-dev/project/secrets-scanner
Project/secrets scanner
2026-02-22 20:08:23 -05:00
CarterPerez-dev 294169a222 feat: Complete Go secrets scanner - Portia 2026-02-22 20:02:38 -05:00
Carter Perez 4f03fcf08f
Merge pull request #94 from CarterPerez-dev/dependabot/uv/PROJECTS/beginner/network-traffic-analyzer/pillow-12.1.1
Bump pillow from 12.1.0 to 12.1.1 in /PROJECTS/beginner/network-traffic-analyzer
2026-02-22 19:50:55 -05:00
dependabot[bot] e619f00cb2
Bump pypdf in /PROJECTS/beginner/metadata-scrubber-tool
Bumps [pypdf](https://github.com/py-pdf/pypdf) from 6.6.2 to 6.7.1.
- [Release notes](https://github.com/py-pdf/pypdf/releases)
- [Changelog](https://github.com/py-pdf/pypdf/blob/main/CHANGELOG.md)
- [Commits](https://github.com/py-pdf/pypdf/compare/6.6.2...6.7.1)

---
updated-dependencies:
- dependency-name: pypdf
  dependency-version: 6.7.1
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-23 00:47:04 +00:00
dependabot[bot] b07dfdfc08
Bump pillow in /PROJECTS/beginner/network-traffic-analyzer
Bumps [pillow](https://github.com/python-pillow/Pillow) from 12.1.0 to 12.1.1.
- [Release notes](https://github.com/python-pillow/Pillow/releases)
- [Changelog](https://github.com/python-pillow/Pillow/blob/main/CHANGES.rst)
- [Commits](https://github.com/python-pillow/Pillow/compare/12.1.0...12.1.1)

---
updated-dependencies:
- dependency-name: pillow
  dependency-version: 12.1.1
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-23 00:46:57 +00:00
deniskhud d67499febf add: cpp version, moved code to separate directories 2026-02-22 13:38:02 +01:00
Carter Perez b97607d868
Update README with project description and credits 2026-02-20 06:00:55 -05:00
Carter Perez 9e018d9f0a
Update README with project developer credit 2026-02-20 06:00:09 -05:00
CarterPerez-dev e7d0fae144 issue 77 pt3 final lints 2026-02-18 20:15:21 -05:00
CarterPerez-dev 7168174006 issue 77 pt3 golangci 2026-02-18 20:08:20 -05:00
CarterPerez-dev 47432c8ce4 issue 77 pt2 ruff 2026-02-18 19:47:47 -05:00
CarterPerez-dev 95cc222302 issue 77 2026-02-18 18:53:15 -05:00
Carter Perez a55fa8a5fd
Update README.md 2026-02-14 03:06:22 -05:00
CarterPerez-dev b04907cad8 feat: simple c2 beacon complete 2026-02-14 03:02:43 -05:00
Carter Perez 8c289aadd1
Fix code block formatting in README.md 2026-02-11 07:46:09 -05:00
CarterPerez-dev f83f92545c - Standardize READMEs and add justfiles across all 14 projects
- Add headers, badges, learn module pointers, just command runner tips, and AGPLv3 licenses etc. etc.
- Add missing Justfiles in some projects
- Update pre commit hook and add TODO
2026-02-11 07:01:25 -05:00
CarterPerez-dev 9205d3ce4d feat: complete base64tool 2026-02-09 07:26:42 -05:00
CarterPerez-dev a33423aa16 feat: complete network-traffic-analyzer 2026-02-01 20:26:28 -05:00
CarterPerez-dev 218305e722 add and create all learn/ folders for beginner projects 2026-01-31 15:13:31 -05:00
CarterPerez-dev a7cae3aa0f Phase 1.1: Organize PROJECTS by difficulty level
- Create beginner/, intermediate/, advanced/ folders in PROJECTS/
- Move all existing projects to appropriate difficulty folders
- Update all README.md links to reflect new structure
- Update contributor links
- Fix pyrightconfig.json trailing comma

Projects organized:
Beginner (6): simple-port-scanner, keylogger, caesar-cipher, dns-lookup, metadata-scrubber-tool, simple-vulnerability-scanner
Intermediate (2): api-security-scanner, docker-security-audit
Advanced (4): api-rate-limiter, encrypted-p2p-chat, bug-bounty-platform, Aenebris
2026-01-29 02:41:15 -05:00