Skip to content
Merged
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
21 changes: 21 additions & 0 deletions content/projects/driftguard.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,27 @@ The repository states the current gaps explicitly:

These limitations are part of the engineering evidence. Hiding them would make the case study weaker, not stronger.

## Executed AWS drift proof — September 18, 2026

The repository also has an [actual AWS-backed GitHub Actions execution](https://github.com/EdwinJdevops/driftguard/actions/runs/35343278654), separate from the inventory-failure unit tests.

That completed run used temporary AWS credentials and Terraform to create a disposable **AWS Systems Manager parameter**. It then changed the parameter through the AWS API outside Terraform, ran `terraform plan -refresh-only`, converted the resulting provider plan to a DriftGuard evidence bundle, verified sensitive values were omitted, and destroyed the test resource.

The job logs reported the following selected evidence fields:

- Resource address: `aws_ssm_parameter.proof`
- Change action: `update`
- Changed paths: `/value` and `/version`
- Redaction verification: `true`

These are fields from the run's output, **not a claim that this list is a separately published artifact**. The run concluded successfully, including the cleanup step that checked the disposable parameter no longer existed.

**What it establishes:** one real Terraform AWS provider refresh, one deliberately introduced out-of-band parameter change, successful evidence generation and value redaction, and cleanup under the tested environment.

**What it does not establish:** that the entire FastAPI collector successfully scans live multi-account infrastructure, that every AWS resource type is supported end to end, or that production scheduling, throughput and recovery guarantees exist. In particular, the SSM parameter exercised the *provider-native evidence path*; it is not proof of the application's separate EC2/S3/RDS inventory collection.

[Examine the executed AWS workflow and its logs](https://github.com/EdwinJdevops/driftguard/actions/runs/35343278654).

## Failure case: incomplete AWS inventory

An AWS collector that fails midway through enumeration must not return an empty resource list. An empty result can mean "no resources exist", while an exception or partial API response means "the system does not know". Conflating those states can produce false deletion findings.
Expand Down
12 changes: 9 additions & 3 deletions layouts/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -64,14 +64,20 @@ <h3><a href="{{ .RelPermalink }}">{{ .Title }}</a></h3>
<p class="public-record-intro">DriftGuard's AWS collector could not safely treat an incomplete inventory as an empty account. A missed resource and an unknown result have different meanings.</p>
<div class="proof-grid public-record-grid">
<article>
<p class="card-kicker">MERGED CODE / REGRESSION COVERAGE</p>
<p class="card-kicker">01 / EXECUTED AWS PROVIDER DRIFT</p>
<h3>An out-of-band AWS change, detected and redacted.</h3>
<p>A successful GitHub Actions run created a disposable AWS Systems Manager parameter with Terraform, changed it outside Terraform, detected the change using a refresh-only plan, and verified a redacted evidence record before cleanup.</p>
<a href="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/EdwinJdevops/driftguard/actions/runs/35343278654">Examine actual AWS execution logs ↗</a>
</article>
<article>
<p class="card-kicker">02 / MERGED FAILURE-HANDLING REGRESSION</p>
<h3>Stop an unsafe scan instead of reporting false deletions.</h3>
<p>PR #3 adds failure handling for interrupted AWS inventory collection and preserves indexed Terraform resource identities. The change includes regression tests and was merged into DriftGuard's main branch.</p>
<a href="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/EdwinJdevops/driftguard/pull/3">Inspect source changes and test evidence ↗</a>
<a href="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/EdwinJdevops/driftguard/pull/3">Inspect source changes and regression tests ↗</a>
</article>
</div>
<div class="public-record-bottom">
<p>This is a public project implementation, not a client testimonial. The tests establish the specified failure behavior—not a production availability guarantee.</p>
<p>This is public engineering evidence, not a client testimonial. The AWS proof exercises a bounded provider-native workflow; PR #3 tests inventory-failure handling. Neither constitutes a production availability guarantee.</p>
<a class="text-link" href="{{ "projects/driftguard/" | relURL }}">Full DriftGuard case study ↗</a>
</div>
</section>
Expand Down
18 changes: 18 additions & 0 deletions layouts/portfolio/list.html
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,24 @@ <h1 id="profile-name">About the <span class="profile-period">work.</span></h1>
<div><span>06 / SECURITY</span><h3>OIDC · IAM/RBAC · Scanning</h3><p>Permission boundaries, Trivy, Kubescape, Falco and reviewable remediation proposals.</p></div>
</div>
</section>
<section class="shell section-block profile-credentials" aria-labelledby="credentials-heading">
<div class="home-section-title">
<div><p class="eyebrow">CREDENTIALS / STRUCTURED TRAINING</p><h2 id="credentials-heading">Education and certification.</h2></div>
</div>
<div class="credential-entries">
<article>
<p class="card-kicker">MICROSOFT / FUNDAMENTALS</p>
<h3>Microsoft Certified: Azure Fundamentals (AZ-900)</h3>
<p>Certification reported as earned in my CV. A public credential-verification link has not yet been added.</p>
</article>
<article>
<p class="card-kicker">ALX AFRICA / COMPLETED PROGRAMME</p>
<h3>Cloud DevOps Engineering Programme</h3>
<p>Completed project-based training in cloud infrastructure, Linux, containers, CI/CD and infrastructure as code, with team-coordination responsibilities.</p>
</article>
</div>
<p class="credential-source-note">These entries reflect my declared CV record; they are not presented as independently authenticated issuer records. No Cisco certification or uncompleted exam is listed here.</p>
</section>
<section class="shell section-block profile-career" aria-labelledby="career-heading">
<div class="home-section-title"><div><p class="eyebrow">PROFESSIONAL EXPERIENCE</p><h2 id="career-heading">Work and responsibilities.</h2></div><a class="text-link" href="{{ "contact/" | relURL }}">Discuss a role ↗</a></div>
<ol class="career-timeline">
Expand Down
15 changes: 13 additions & 2 deletions scripts/visual_audit.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -257,11 +257,20 @@ const fs = require("node:fs");
if (await introLink.count() !== 1) failures.push("About route missing from hero");
const homepageEmail = identity.locator('.footer-email[href^="mailto:edwinjonathanchibuike123@gmail.com"]').first();
if (await homepageEmail.count() !== 1) failures.push("Verified contact link missing from footer");
const portraitWidth = await identity.locator(".home-portrait").evaluate(el => el.getBoundingClientRect().width);
if (portraitWidth > 230)
failures.push("Legacy portrait still dominates the homepage: "+viewport.width+"px viewport, "+portraitWidth+"px photo");
const homeOverflow = await identity.evaluate(() => document.documentElement.scrollWidth - document.documentElement.clientWidth);
if (homeOverflow > 2) failures.push("Identity homepage overflow at "+viewport.width+"px: "+homeOverflow);
await identity.goto("http://127.0.0.1:4173/portfolio/", {waitUntil:"domcontentloaded"});
if (await identity.locator(".tech-stack-grid > div").count() !== 6)
failures.push("Expected six technical stack groups: "+viewport.width);
const credentialItems = identity.locator(".credential-entries article");
if (await credentialItems.count() !== 2)
failures.push("Expected CV-recorded Microsoft and ALX entries: "+viewport.width);
const credentialText = await identity.locator(".profile-credentials").innerText();
if (!credentialText.includes("Azure Fundamentals (AZ-900)") || !credentialText.includes("Cloud DevOps Engineering Programme") || !credentialText.includes("verification link"))
failures.push("Credentials or missing public-verification disclosure absent: "+viewport.width);
if (await identity.locator(".career-timeline > li").count() !== 4)
failures.push("Expected four CV-reported professional roles: "+viewport.width);
const aboutText = await identity.locator(".profile-confidential").innerText();
Expand All @@ -278,8 +287,10 @@ const fs = require("node:fs");
const publicEvidence = await browser.newPage({ viewport, reducedMotion:"reduce" });
publicEvidence.on("pageerror", error => failures.push("Public evidence "+viewport.width+" JS exception: "+error.message));
await publicEvidence.goto("http://127.0.0.1:4173/", {waitUntil:"domcontentloaded"});
if (await publicEvidence.locator(".public-record-grid article").count() !== 1)
failures.push("Only independent DriftGuard failure case should lead homepage: "+viewport.width);
if (await publicEvidence.locator(".public-record-grid article").count() !== 2)
failures.push("Both executed AWS drift proof and independent inventory regression must appear: "+viewport.width);
if (await publicEvidence.locator('.home-public-record a[href="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/EdwinJdevops/driftguard/actions/runs/35343278654"]').count() !== 1)
failures.push("Executed AWS provider evidence run missing: "+viewport.width);
if (await publicEvidence.locator('.home-public-record a[href="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/EdwinJdevops/driftguard/pull/3"]').count() < 1)
failures.push("Missing merged external DriftGuard PR evidence: "+viewport.width);
const evidenceText = await publicEvidence.locator(".home-public-record").innerText();
Expand Down
16 changes: 16 additions & 0 deletions static/css/main.css
Original file line number Diff line number Diff line change
Expand Up @@ -638,3 +638,19 @@ a:focus-visible,summary:focus-visible{outline:3px solid var(--accent);outline-of
.work-research-list article>.editorial-read{margin-top:10px}
.home-availability{font-size:12px}
}

/* Credentials: factual, non-decorative CV record with clear validation boundary. */
.profile-credentials{border-top:1px solid var(--line)}
.credential-entries{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:0;border-top:1px solid var(--line);border-left:1px solid var(--line)}
.credential-entries article{min-width:0;padding:25px;border-right:1px solid var(--line);border-bottom:1px solid var(--line)}
.credential-entries h3{font-size:clamp(22px,2.5vw,33px);font-weight:550;line-height:1.15;letter-spacing:-.045em;margin:16px 0}
.credential-entries article>p:last-child,.credential-source-note{font-size:13px;line-height:1.7;color:var(--muted)}
.credential-source-note{max-width:850px;margin-top:18px}
/* Retain the authentic portrait but let technical work, not an old photograph, dominate. */
.home-portrait{width:min(100%,225px);max-width:225px;align-self:center;margin-inline:auto}
.home-portrait figcaption{background:#09131d;color:#fff}
.home-portrait img{width:100%;height:auto;max-height:315px;aspect-ratio:4/5;object-fit:cover;object-position:center 22%;border-radius:3px}
.home-intro:has(.home-portrait) .infra-scene{min-height:290px}
@media(min-width:821px){.home-intro:has(.home-portrait){grid-template-columns:minmax(0,1fr) 235px}}
@media(max-width:820px){.home-portrait{width:175px;max-width:175px;margin:0}.home-portrait img{max-height:220px;aspect-ratio:4/5}.home-intro:has(.home-portrait) .infra-scene{min-height:280px}}
@media(max-width:640px){.credential-entries{grid-template-columns:1fr}.credential-entries article{padding:20px}}
Loading