Sections in this lesson

Model, Tool & Dependency Supply Chain

Model files, datasets, tool packages and CI actions are distinct supply-chain inputs.

Reviewed: · Content owner: Interview Preparator

What you will learn

  • Verify what you install
  • Separate integrity from provenance
  • Carry supply-chain changes into evaluation

Before you start

The flow

  1. 1.Identify scope
  2. 2.Inspect evidence
  3. 3.Apply host control
  4. 4.Run negative cases
  5. 5.Compare benign behavior
  6. 6.Review remaining risks

Verify what you install

Model files, datasets, tool packages and CI actions are distinct supply-chain inputs. Record exact versions, origin, digest and approval owner before execution. A model-generated package name may be invented or impersonated.

The approvedArtifact helper compares bytes against an already trusted SHA-256 value. It does not authenticate the publisher or establish that approved code is safe.

Separate integrity from provenance

A matching checksum proves correlation with expected bytes. A signed attestation adds issuer/build context only if that identity and workflow are verified. Neither replaces code review, dependency analysis or restricted runtime credentials.

Pinning a dependency is a reproducibility control, not a promise that its pinned version has no vulnerability. Monitor updates and document the decision to retain or replace it.

Carry supply-chain changes into evaluation

Updating a model, package, prompt or corpus changes a dependency of the behavior. Run both normal tasks and security regressions, preserve the previous artifact and invalidate approvals tied to older bytes.

The fixture tests changed bytes and expected digests locally. Production package registries, publisher signatures and artifact promotion are not configured by this lesson.

Try it

policy.mjs
javascript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import { createHash, timingSafeEqual } from 'node:crypto';
export function payloadDigest(call) {
// Canonical ordered fields; extra or ambiguous fields cannot bypass payload binding.
if (!call || Object.keys(call).sort().join(',') !== 'action,resource' || !['readPlan','completePlan'].includes(call.action)
|| !/^PLAN-[0-9]+$/.test(call.resource)) throw new Error('Invalid call');
return createHash('sha256').update(JSON.stringify([call.action,call.resource])).digest('hex');
}
export function authorize(scope, call, resource, grant, now, epoch = 1) {
const digest = payloadDigest(call);
if (!Number.isFinite(now) || !Number.isSafeInteger(epoch)) return 'DENY';
if (!scope || !resource || resource.id !== call.resource || resource.tenant !== scope.tenant || !resource.readers.includes(scope.user)) return 'DENY';
if (call.action === 'readPlan') return 'ALLOW';
// Grant comes from a trusted server-side approval store, never from model output.
if (!grant || !Number.isFinite(grant.expiresAt) || grant.revoked || grant.epoch !== epoch || grant.expiresAt <= now || grant.tenant !== scope.tenant
|| grant.user !== scope.user || grant.runId !== scope.runId || grant.digest !== digest) return 'APPROVAL_REQUIRED';
return 'ALLOW';
}
export function escapedText(value) {
if (typeof value !== 'string' || value.length > 4000) throw new Error('Invalid output text');
return value.replaceAll('&','&amp;').replaceAll('<','&lt;').replaceAll('>','&gt;').replaceAll('"','&quot;').replaceAll("'",'&#39;');
}
export function allowedUrl(value, hosts) {
try { const url = new URL(value); return url.protocol === 'https:' && !url.username && !url.password && url.port === '' && hosts.includes(url.hostname); }
catch { return false; }
}
export function trustedToken(claims, policy, now) {
// Claims must already be cryptographically verified by an auth adapter.
return claims?.issuer === policy.issuer && claims?.audience === policy.audience && claims?.expiresAt > now
&& claims?.tenant === policy.tenant && Array.isArray(claims?.scopes) && claims.scopes.includes(policy.scope);
}
export function approvedArtifact(bytes, expectedSha256) {
const actual = createHash('sha256').update(bytes).digest();
if (!/^[a-f0-9]{64}$/.test(expectedSha256 ?? '')) return false;
return timingSafeEqual(actual,Buffer.from(expectedSha256,'hex'));
}

Run npm run examples:security and npm run eval:security. This source is a deterministic teaching fixture; it does not prove live model safety, NLI, production auth or OS/network isolation.

Interview questions

What boundary matters in Model, Tool & Dependency Supply Chain?

The approvedArtifact helper compares bytes against an already trusted SHA-256 value. It does not authenticate the publisher or establish that approved code is safe.

What does the executable fixture demonstrate?

Pinning a dependency is a reproducibility control, not a promise that its pinned version has no vulnerability. Monitor updates and document the decision to retain or replace it.

What remains a production integration responsibility?

The fixture tests changed bytes and expected digests locally. Production package registries, publisher signatures and artifact promotion are not configured by this lesson.

Practice

Extend one negative scenario and its authorized positive counterexample for Model, Tool & Dependency Supply Chain.

Check your solution

  • State the trusted input and effect boundary.
  • Assert the expected status and observed effect.
  • Report coverage and limitations without claiming production readiness.

Official references

Continue learning

Category sequence

AI Security

Lesson 9 / 14

Was this lesson useful?

Change notes: 2026-10-31: security/reliability curriculum and source-bound fixture.