Dokümanlar menüsü
Taint guard
untrusted: true ile işaretlenmiş bir araç başarılı olduğunda, çalıştırma tainted (kirlenmiş) olarak işaretlenir. Bu noktadan SONRAKİ her yan-etki araç çağrısı merdiven (off → warn → reflect → block → suspend) tarafından yönetilir — modeli yeniden düşünmeye yönlendirir ya da çağrıyı doğrudan durdurur.
Ne işe yarar / ne zaman kullanılır#
Bir web sayfası çeken, bir e-posta okuyan ya da bir belge açan bir ajan, bir saldırganın yazmış olabileceği içeriği okuyordur — bir prompt injection. Taint guard, çalışma-zamanı cevabıdır: o içeriği üreten aracı untrusted: true ile işaretleyin; başarılı olduktan sonraki herhangi bir YAN-ETKİ araç çağrısı (sendMail, transfer, …), enjekte edilmiş talimatlara göre hareket edemeden önce merdiven tarafından yakalanır. Taint monoton ve ilk-kazanırdır — bir çalıştırma bir kez kirlendiğinde kirli kalır, ve kaydedilen kaynak her zaman ilk güvenilmeyen çağrıdır.
Merdiven#
Cevabı limits.taintedSideEffects ile belirlersiniz (varsayılan warn). Seviyeler sertlik bakımından yükselir:
'off'Zorlama yok — taint hot path'te okunmaz bile.
'warn'Eskisi gibi yürür ama taint kaynağını adlandıran, journal'lanan bir incident yazar.
'reflect'Distinct (araç, argümanlar) çifti başına TEK bir nudge iletir — duplicate guard'ın 'reflect'i gibi tüm çalıştırma için tek seferlik DEĞİLDİR. Nudge'dan SONRAKİ özdeş tekrar YÜRÜR ('warn' kaydı olarak journal'lanır); block'a yükselmez. Bu, duplicate guard'ın 'reflect'inden kasıtlı olarak farklıdır.
'block'Çağrıyı TaintedSideEffectError ile durdurur.
'suspend'Askıya alınmış bir kayıt yazar → çağrı Approvals akışına düşer, ve insan sebepte taint kaynağını görür.
Kurulum / import#
Guard @gnldev/durable içinde yaşar ve çalıştırma başına RunLimits ile yapılandırılır:
import { createGnl, TaintedSideEffectError, readRunTaint, type RunLimits } from '@gnldev/durable';Adım adım kullanım#
OUTPUT'u dışarıdan gelen, saldırgan tarafından yazılabilir içerik olan aracı untrusted: true ile işaretleyin. Başarılı olduğunda çalıştırma tainted işaretlenir; sonraki bir yan-etki aracı block altında TaintedSideEffectError fırlatır:
// 'fetchPage' is marked untrusted: true — its OUTPUT is external, attacker-authorable
// content (whatever the fetched page contains). A successful call taints the run.
const fetchPage = { execute: async (a) => fetch(a.url).then((r) => r.text()), untrusted: true };
const limits: RunLimits = { taintedSideEffects: 'block' };
try {
// A prompt-injected instruction inside the fetched page tells the model to email an
// attacker. 'sendMail' is a side effect firing AFTER untrusted content entered the run.
await gnl.run('research', { runId: 'task-9', prompt: 'summarize this page', limits });
} catch (e) {
if (e instanceof TaintedSideEffectError) {
// e.detail.taintSource → which tool call introduced the taint
console.log('tainted side effect blocked:', e.detail.toolName, e.detail.taintSource);
}
}Modelin kendini toparlamasını tercih ediyorsanız reflect kullanın — ancak davranışın çalıştırma-başına değil çağrı-başına olduğuna dikkat edin:
// Unlike the duplicate guard's 'reflect', the nudge here is per distinct (tool, args)
// pair. An identical retry AFTER the nudge EXECUTES (journaled as a 'warn' record) —
// it does NOT escalate to a block.
const limits: RunLimits = { taintedSideEffects: 'reflect' };Bir block'a sebep olan taint'i okuyun, ya da bir aracın çıktısını enjekte edilmiş talimatlar için inceleyen bir processor'dan taint'i dinamik olarak belirleyin:
// Inspect the taint that caused a block, or set taint dynamically from a processor
// (e.g. a tool that scans its own output for injected instructions):
const taint = await readRunTaint(journal, runId);
if (taint) console.log('tainted by', taint.toolName, taint.source, taint.reason);
await markRunTainted(journal, runId, {
toolCallId,
toolName: 'customScan',
source: 'processor',
reason: 'injection pattern detected',
});Turlar arası taint: taintScope#
Varsayılan olarak taint tek bir çalıştırmayla sınırlıdır — bir konuşmanın 2. turu, 1. tur tainted olsa bile temiz başlar. Aynı threadId üzerindeki turlar arasında taint'i yaymak için limits.taintScope'u thread olarak ayarlayın (varsayılan run): bir çalıştırma bir kez tainted olduğunda, aynı thread'i paylaşan SONRAKİ her çalıştırma bu taint'i devralır — böylece 1. turun kirli bir sayfa çektiği ve 2. turun, untrusted aracı hiç çağırmamış bir çalıştırmada bu içerik üzerinde hareket ettiği açık kapanır.
// Turn 1: fetchPage taints THIS run. Turn 2 is a SEPARATE run on the same threadId
// asking the model to act on what was fetched — without taintScope: 'thread' the new
// run starts clean and the injected instruction is not gated.
const limits: RunLimits = { taintedSideEffects: 'block', taintScope: 'thread' };
await gnl.run('research', { runId: 'turn-1', threadId: 'conv-42', prompt: 'fetch this page', limits });
// Later, same conversation — turn-2 inherits the taint from turn-1, so a side-effect
// tool call here also runs the ladder, even though turn-2 never called fetchPage:
await gnl.run('research', { runId: 'turn-2', threadId: 'conv-42', prompt: 'send it to my team', limits });Taint'in süresi: taintLifetime#
Devralınan thread taint'i varsayılan olarak persistent'tir — bir thread bir kez kirlendiğinde sonsuza kadar kirli kalır. Devralınan taint'in, kirleten içerik o çalıştırmanın yüklü mesajlarında (recent + recalled) artık görünmediğinde VE working memory boş olduğunda SÜRESİNİN DOLMASINI istiyorsanız limits.taintLifetime'ı content-window olarak ayarlayın. Sonraki bir recall adımı taint'i yeniden canlandırabilir, working memory ise onu canlı tutar. Yalnızca taintScope: thread ile birlikte anlamlıdır — bir çalıştırmanın kendi taint'i asla süresi dolmaz.
// Same thread, taint inherited from an earlier run — but this run's loaded messages
// (recent + recalled) no longer include the tainting content, and working memory is
// empty: the taint EXPIRES for this run, and the ladder does not fire on it.
const limits: RunLimits = {
taintedSideEffects: 'block',
taintScope: 'thread',
taintLifetime: 'content-window',
};
// If a later recall step pulls the tainting message BACK into this run's context, or
// a tool wrote it to working memory, the taint is revived / kept alive for this run.taintGuardian: bir hassas aracı yalnızca tainted olduğunda kapıla#
taintGuardian bir Guard factory'sidir: ona sensitiveTools ve bir onTainted karar fonksiyonu, ayrıca opsiyonel bir otherwise fallback'i geçirirsiniz. Çalıştırma tainted OLDUĞUNDA VE çağrılan araç sensitiveTools'tan biri OLDUĞUNDA, onTainted çalışır ve kararı kullanılır; diğer her çağrı otherwise'a düşer (varsayılan allow). Pahalı kontrol YALNIZCA tainted bir çalıştırma ile hassas bir aracın kesiştiği noktada çalışır — her çağrıda değil.
import { taintGuardian } from '@gnldev/durable';
// The expensive judge runs ONLY when BOTH conditions hold: the run is tainted AND the
// tool being called is listed in sensitiveTools. Every other call is allowed without
// paying for the check.
const guard = taintGuardian({
sensitiveTools: ['sendMail', 'transfer'],
onTainted: async (call) => {
// call.tainted holds the taint context — set on EVERY GuardCall, not just here.
return { action: 'require-approval', reason: call.tainted?.reason };
},
otherwise: { action: 'allow' },
});
await gnl.run('research', { runId: 'r1', prompt: 'summarize and email it', guard, limits });Sadece taintGuardian değil, herhangi bir guard aynı bağlamı okuyabilir: GuardCall artık RunTaint tipinde opsiyonel bir tainted alanı taşır, çalıştırma tainted olduğunda ayarlanır — böylece elle yazılmış bir guard da doğrudan buna göre dallanabilir.
API referansı#
RunLimits.taintedSideEffectsRunLimits alanı: 'off' | 'warn' | 'reflect' | 'block' | 'suspend'. Varsayılan 'warn'. Güvenilmeyen içerik çalıştırmaya girdikten sonra yapılan bir yan-etki araç çağrısına uygulanır.
RunLimits.taintScopeRunLimits alanı: 'run' | 'thread'. Varsayılan 'run'. 'thread' ile, bir çalıştırmada kaydedilen taint, aynı threadId'yi paylaşan her sonraki çalıştırma tarafından devralınır.
RunLimits.taintLifetimeRunLimits alanı: 'persistent' | 'content-window'. Varsayılan 'persistent'. 'content-window' ile, devralınan thread taint'i, kirleten içerik o çalıştırmanın yüklü mesajlarından (recent + recalled) çıktığında ve working memory boş olduğunda süresi dolar; recall onu yeniden canlandırabilir, working memory canlı tutar. Yalnızca taintScope: 'thread' ile birlikte anlamlıdır.
TaintedSideEffectError'block' altında fırlatılır. detail: { toolName, toolCallId, taintSource: { toolCallId, toolName } }.
AnyTool.untrustedAnyTool alanı: untrusted?: boolean. Çıktısı dışarıdan/saldırgan tarafından yazılabilir içerik olan bir aracı işaretler (bir web fetch, bir e-posta gövdesi, bir belge). Başarı halinde çalıştırma tainted işaretlenir (monoton, ilk-kazanır, journal'lanır).
markRunTaintedmarkRunTainted(journal, runId, taint): idempotent, ilk-kazanır, asla fırlatmaz. Untrusted araçlar için otomatik çağrılır; bir processor'ın taint'i dinamik olarak belirleyebilmesi için export edilir.
readRunTaintreadRunTaint(journal, runId): bir çalıştırma için geçerli taint kaydını okur, çalıştırma tainted değilse undefined döner.
RunTaintRunTaint = { at, toolCallId, toolName, source: 'tool' | 'processor', reason? }.
taintGuardiantaintGuardian({ sensitiveTools, onTainted, otherwise? }) → Guard. onTainted'ı YALNIZCA çalıştırma tainted VE çağrılan araç sensitiveTools içindeyken çalıştırır; aksi halde otherwise kararını uygular (varsayılan { action: 'allow' }).
GuardCall.taintedGuardCall alanı: tainted?: RunTaint. Çalıştırma tainted olduğunda her GuardCall'da ayarlanır — böylece sadece taintGuardian değil, herhangi bir guard taint bağlamına göre dallanabilir.
reflect'i distinct (araç, argümanlar) çifti başına BİR KEZ nudge verir, ve nudge'dan SONRAKİ özdeş tekrar YÜRÜR — 'warn' kaydı olarak journal'lanır, asla block'a yükselmez. Bu, duplicate-guard'ın reflect'inden kasıtlı olarak farklıdır; orada nudge'dan sonraki özdeş tekrar block'a yükselir. Merdiven seviyesini bu ayrımı akılda tutarak seçin.off değil warn'dır: davranış değişmez ama her tainted yan-etki, taint kaynağıyla birlikte journal'lanan bir incident olarak adlandırılır — böylece bir operatör, konsol satırına buharlaşmak yerine onu sorgulayabilir.duplicate-guard, suspend'in yönlendirdiği onay akışı için human-in-loop-approvals, ve bunun gibi merdivenlerin bir politikaya nasıl birleştiği için guard-policy sayfalarına bakın.