llm-trust-guard 4.18.0 → 4.18.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +19 -0
- package/README.md +10 -2
- package/dist/guards/heuristic-analyzer.js +1 -1
- package/dist/index.mjs +3 -3
- package/package.json +8 -4
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,25 @@ All notable changes to `llm-trust-guard` will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [4.18.1] - 2026-04-20
|
|
9
|
+
|
|
10
|
+
### Fixed — Metadata and README Accuracy
|
|
11
|
+
|
|
12
|
+
- **`package.json` description**: said "22 protection layers" but actual count is 34 guards. Fixed
|
|
13
|
+
- **README guard count**: "All 31 Guards" heading and "same 31 guards" link description were stale after v4.14.0 added three multi-agent guards. Bumped to 34
|
|
14
|
+
- **README multi-agent table**: SpawnPolicyGuard, DelegationScopeGuard, TrustTransitivityGuard were added to `src/guards/` in v4.14.0 but never listed in the README guard table. Added under new "Multi-Agent Guards (OWASP ASI07)" section
|
|
15
|
+
- **`heuristic-analyzer.ts`**: removed two `as any` casts by introducing `SynonymCategory` union type and `SynonymFeatureKey` template literal type. Runtime behavior unchanged; typecheck strengthened
|
|
16
|
+
|
|
17
|
+
### Changed — Package Hygiene
|
|
18
|
+
|
|
19
|
+
- Added `publishConfig.provenance: true` for npm provenance attestation
|
|
20
|
+
- Bumped `typescript` devDep to `^5.7.0` (was `^5.3.2`)
|
|
21
|
+
- Bumped `@types/node` devDep to `^22.10.0` (was `^20.10.0`)
|
|
22
|
+
- `vitest`/`@vitest/coverage-v8` intentionally kept at `^1.6.0` — the jump to 3.x has breaking config/snapshot changes and deserves its own release cycle
|
|
23
|
+
|
|
24
|
+
### Stats
|
|
25
|
+
- 34 guards, 695 tests, <5ms latency, zero dependencies (unchanged)
|
|
26
|
+
|
|
8
27
|
## [4.18.0] - 2026-04-10
|
|
9
28
|
|
|
10
29
|
### Removed — TF-IDF Built-in Classifier
|
package/README.md
CHANGED
|
@@ -122,7 +122,7 @@ const toolResult = guard.validateToolResult('search', toolOutput);
|
|
|
122
122
|
const output = guard.filterOutput(llmResponse, session.role);
|
|
123
123
|
```
|
|
124
124
|
|
|
125
|
-
## All
|
|
125
|
+
## All 34 Guards
|
|
126
126
|
|
|
127
127
|
### Input Guards (before LLM)
|
|
128
128
|
|
|
@@ -175,6 +175,14 @@ const output = guard.filterOutput(llmResponse, session.role);
|
|
|
175
175
|
| AgentSkillGuard | Malicious plugin/tool detection (OpenClaw) | Backdoor signatures + typosquatting |
|
|
176
176
|
| SessionIntegrityGuard | Session hijacking, permission escalation | Binding + sequence + timeout |
|
|
177
177
|
|
|
178
|
+
### Multi-Agent Guards (OWASP ASI07)
|
|
179
|
+
|
|
180
|
+
| Guard | Purpose | Detection |
|
|
181
|
+
|-------|---------|-----------|
|
|
182
|
+
| SpawnPolicyGuard | Agent spawn policy enforcement | CSP-style allowlists, max delegation depth |
|
|
183
|
+
| DelegationScopeGuard | Agent-to-agent scope downscoping | OAuth-style parent-child scope subset |
|
|
184
|
+
| TrustTransitivityGuard | Trust chain validation | X.509-style chain depth + min trust score |
|
|
185
|
+
|
|
178
186
|
### Pluggable Detection
|
|
179
187
|
|
|
180
188
|
| Component | Purpose |
|
|
@@ -248,7 +256,7 @@ MIT
|
|
|
248
256
|
|
|
249
257
|
## Links
|
|
250
258
|
|
|
251
|
-
- [Python package (PyPI)](https://pypi.org/project/llm-trust-guard/) — same
|
|
259
|
+
- [Python package (PyPI)](https://pypi.org/project/llm-trust-guard/) — same 34 guards, zero dependencies
|
|
252
260
|
- [OWASP Top 10 for LLMs 2025](https://genai.owasp.org/resource/owasp-top-10-for-llm-applications-2025/)
|
|
253
261
|
- [OWASP Top 10 for Agentic Applications 2026](https://genai.owasp.org/resource/owasp-top-10-for-agentic-applications-for-2026/)
|
|
254
262
|
- [MITRE ATLAS](https://atlas.mitre.org/)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.HeuristicAnalyzer=void 0;const SYNONYM_SETS={ignore:{keywords:new Set(["ignore","disregard","overlook","neglect","skip","bypass","omit","dismiss","override","overrule","forget","abandon","drop","ditch","scrap","discard","set aside","pay no attention","turn a blind eye","look past","brush off","shrug off","wave away","cast aside","put aside","leave behind","throw out","toss out"]),weight:.3},urgent:{keywords:new Set(["urgent","immediate","immediately","asap","emergency","critical","time-sensitive","pressing","crucial","dire","life-threatening","now","hurry","rush","quick","fast","right away","without delay","time is running out","deadline"]),weight:.15},incentive:{keywords:new Set(["excellent","fantastic","nice","awesome","brilliant","great","amazing","wonderful","superb","outstanding","reward","tip","bonus","payment","compensate","grateful","appreciate"]),weight:.1},covert:{keywords:new Set(["secret","hidden","covert","quiet","silent","discreetly","stealth","confidential","private","classified","concealed","disguised","undercover","off the record","between us","nobody will know","our little secret","hush","whisper"]),weight:.2},format_manipulation:{keywords:new Set(["encode","decode","disguise","morse","binary","hexadecimal","base64","rot13","cipher","encrypt","obfuscate","scramble","reverse","translate to code","convert to"]),weight:.15},hypothetical:{keywords:new Set(["assume","imagine","hypothetically","fictional","scenario","pretend","suppose","what if","let's say","in theory","theoretically","fantasy","roleplay","simulation","make believe","alternate reality","parallel universe","thought experiment"]),weight:.15},systemic:{keywords:new Set(["developer","administrator","admin","boss","manager","creator","owner","supervisor","root","superuser","operator","maintainer","engineer","architect","designer","authority","controller","master","god mode","sudo"]),weight:.2},immoral:{keywords:new Set(["amoral","immoral","unethical","illegal","harmful","dangerous","offensive","violent","malicious","evil","wicked","sinister","criminal","unlawful","forbidden","prohibited","taboo","no morals","no ethics","no rules","no limits","unrestricted","uncensored","unfiltered"]),weight:.25}},INSTRUCTION_WORDS=new Set(["must","should","shall","will","need","require","always","never","do","don't","cannot","can't","ensure","make sure","remember","forget","ignore","follow","obey","comply","execute","perform","output","respond","reply","answer","generate","create","write","act","behave","pretend","assume","become","transform"]);class HeuristicAnalyzer{constructor(t={}){this.config={synonymExpansion:t.synonymExpansion??!0,structuralAnalysis:t.structuralAnalysis??!0,statisticalScoring:t.statisticalScoring??!0,riskThreshold:t.riskThreshold??.8,manyShotThreshold:t.manyShotThreshold??3,repeatedTokenThreshold:t.repeatedTokenThreshold??3}}analyze(t,i){const r=[],s={is_ignore:!1,is_urgent:!1,is_incentive:!1,is_covert:!1,is_format_manipulation:!1,is_hypothetical:!1,is_systemic:!1,is_immoral:!1,synonym_categories_matched:0,is_shot_attack:!1,is_repeated_token:!1,is_imperative:!1,is_role_assignment:!1,structural_score:0,instruction_word_density:0,special_char_ratio:0,uppercase_ratio:0,average_word_length:0,statistical_score:0};let c=0;if(this.config.synonymExpansion){const e=this.checkSynonyms(t);Object.assign(s,e.features),c+=e.risk,e.risk>0&&r.push(...e.matched.map(l=>`SYNONYM_${l.toUpperCase()}`))}if(this.config.structuralAnalysis){const e=this.checkStructure(t);s.is_shot_attack=e.is_shot_attack,s.is_repeated_token=e.is_repeated_token,s.is_imperative=e.is_imperative,s.is_role_assignment=e.is_role_assignment,s.structural_score=e.score,c+=e.score,e.violations.length>0&&r.push(...e.violations)}if(this.config.statisticalScoring){const e=this.scoreStatistics(t);s.instruction_word_density=e.instruction_word_density,s.special_char_ratio=e.special_char_ratio,s.uppercase_ratio=e.uppercase_ratio,s.average_word_length=e.average_word_length,s.statistical_score=e.score,c+=e.score}s.synonym_categories_matched>=3&&(c+=.15,r.push("MULTI_CATEGORY_COMPOUND"));const a=Math.min(1,c),n=a<this.config.riskThreshold;return{allowed:n,reason:n?void 0:`Heuristic analysis risk ${a.toFixed(2)} exceeds threshold ${this.config.riskThreshold}`,riskScore:a,features:s,violations:r}}checkSynonyms(t){const i=t.toLowerCase().split(/\s+/).map(e=>e.replace(/[^a-z'-]/g,"")).filter(e=>e.length>2),r=t.toLowerCase(),s={};let c=0;const a=[];let n=0;for(const[e,{keywords:l,weight:
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.HeuristicAnalyzer=void 0;const SYNONYM_SETS={ignore:{keywords:new Set(["ignore","disregard","overlook","neglect","skip","bypass","omit","dismiss","override","overrule","forget","abandon","drop","ditch","scrap","discard","set aside","pay no attention","turn a blind eye","look past","brush off","shrug off","wave away","cast aside","put aside","leave behind","throw out","toss out"]),weight:.3},urgent:{keywords:new Set(["urgent","immediate","immediately","asap","emergency","critical","time-sensitive","pressing","crucial","dire","life-threatening","now","hurry","rush","quick","fast","right away","without delay","time is running out","deadline"]),weight:.15},incentive:{keywords:new Set(["excellent","fantastic","nice","awesome","brilliant","great","amazing","wonderful","superb","outstanding","reward","tip","bonus","payment","compensate","grateful","appreciate"]),weight:.1},covert:{keywords:new Set(["secret","hidden","covert","quiet","silent","discreetly","stealth","confidential","private","classified","concealed","disguised","undercover","off the record","between us","nobody will know","our little secret","hush","whisper"]),weight:.2},format_manipulation:{keywords:new Set(["encode","decode","disguise","morse","binary","hexadecimal","base64","rot13","cipher","encrypt","obfuscate","scramble","reverse","translate to code","convert to"]),weight:.15},hypothetical:{keywords:new Set(["assume","imagine","hypothetically","fictional","scenario","pretend","suppose","what if","let's say","in theory","theoretically","fantasy","roleplay","simulation","make believe","alternate reality","parallel universe","thought experiment"]),weight:.15},systemic:{keywords:new Set(["developer","administrator","admin","boss","manager","creator","owner","supervisor","root","superuser","operator","maintainer","engineer","architect","designer","authority","controller","master","god mode","sudo"]),weight:.2},immoral:{keywords:new Set(["amoral","immoral","unethical","illegal","harmful","dangerous","offensive","violent","malicious","evil","wicked","sinister","criminal","unlawful","forbidden","prohibited","taboo","no morals","no ethics","no rules","no limits","unrestricted","uncensored","unfiltered"]),weight:.25}},INSTRUCTION_WORDS=new Set(["must","should","shall","will","need","require","always","never","do","don't","cannot","can't","ensure","make sure","remember","forget","ignore","follow","obey","comply","execute","perform","output","respond","reply","answer","generate","create","write","act","behave","pretend","assume","become","transform"]);class HeuristicAnalyzer{constructor(t={}){this.config={synonymExpansion:t.synonymExpansion??!0,structuralAnalysis:t.structuralAnalysis??!0,statisticalScoring:t.statisticalScoring??!0,riskThreshold:t.riskThreshold??.8,manyShotThreshold:t.manyShotThreshold??3,repeatedTokenThreshold:t.repeatedTokenThreshold??3}}analyze(t,i){const r=[],s={is_ignore:!1,is_urgent:!1,is_incentive:!1,is_covert:!1,is_format_manipulation:!1,is_hypothetical:!1,is_systemic:!1,is_immoral:!1,synonym_categories_matched:0,is_shot_attack:!1,is_repeated_token:!1,is_imperative:!1,is_role_assignment:!1,structural_score:0,instruction_word_density:0,special_char_ratio:0,uppercase_ratio:0,average_word_length:0,statistical_score:0};let c=0;if(this.config.synonymExpansion){const e=this.checkSynonyms(t);Object.assign(s,e.features),c+=e.risk,e.risk>0&&r.push(...e.matched.map(l=>`SYNONYM_${l.toUpperCase()}`))}if(this.config.structuralAnalysis){const e=this.checkStructure(t);s.is_shot_attack=e.is_shot_attack,s.is_repeated_token=e.is_repeated_token,s.is_imperative=e.is_imperative,s.is_role_assignment=e.is_role_assignment,s.structural_score=e.score,c+=e.score,e.violations.length>0&&r.push(...e.violations)}if(this.config.statisticalScoring){const e=this.scoreStatistics(t);s.instruction_word_density=e.instruction_word_density,s.special_char_ratio=e.special_char_ratio,s.uppercase_ratio=e.uppercase_ratio,s.average_word_length=e.average_word_length,s.statistical_score=e.score,c+=e.score}s.synonym_categories_matched>=3&&(c+=.15,r.push("MULTI_CATEGORY_COMPOUND"));const a=Math.min(1,c),n=a<this.config.riskThreshold;return{allowed:n,reason:n?void 0:`Heuristic analysis risk ${a.toFixed(2)} exceeds threshold ${this.config.riskThreshold}`,riskScore:a,features:s,violations:r}}checkSynonyms(t){const i=t.toLowerCase().split(/\s+/).map(e=>e.replace(/[^a-z'-]/g,"")).filter(e=>e.length>2),r=t.toLowerCase(),s={};let c=0;const a=[];let n=0;for(const[e,{keywords:l,weight:g}]of Object.entries(SYNONYM_SETS)){let h=!1;for(const o of i)if(l.has(o)){h=!0;break}if(!h){for(const o of l)if(o.includes(" ")&&r.includes(o)){h=!0;break}}const d=`is_${e}`;h?(s[d]=!0,c+=g,a.push(e),n++):s[d]=!1}return s.synonym_categories_matched=n,{features:s,risk:c,matched:a}}checkStructure(t){const i=[];let r=0;const s=/(?:Q:|Question:|Human:|User:)[\s\S]*?(?:A:|Answer:|Assistant:|AI:)/gi,a=(t.match(s)||[]).length>=this.config.manyShotThreshold;a&&(r+=.3,i.push("MANY_SHOT_PATTERN"));const n=t.toLowerCase().split(/\s+/).filter(u=>u.length>3),e=new Map;for(const u of n)e.set(u,(e.get(u)||0)+1);const l=Math.max(0,...e.values()),g=l>=this.config.repeatedTokenThreshold&&n.length>10&&l/n.length>.15;g&&(r+=.1,i.push("REPEATED_TOKEN_ATTACK"));const h=t.split(/[.!?\n]+/).filter(u=>u.trim().length>5);let d=0;for(const u of h){const m=u.trim();/^(?:ignore|forget|disregard|override|bypass|reveal|show|tell|give|grant|make|do|don't|never|always|you\s+(?:must|should|will|are|can))/i.test(m)&&d++}const o=h.length>0&&d/h.length>.4;o&&(r+=.15,i.push("HIGH_IMPERATIVE_RATIO"));const _=/you\s+(?:are|will\s+be|shall\s+be|must\s+be)\s+(?:now\s+)?(?:a|an|the|my)\s+/i.test(t)&&/(?:no\s+(?:restrictions|rules|limits)|unrestricted|unfiltered|evil|amoral|can\s+do\s+anything)/i.test(t);return _&&(r+=.25,i.push("ROLE_ASSIGNMENT_WITH_BYPASS")),{is_shot_attack:a,is_repeated_token:g,is_imperative:o,is_role_assignment:_,score:r,violations:i}}scoreStatistics(t){const i=t.split(/\s+/).filter(o=>o.length>0);if(i.length===0)return{instruction_word_density:0,special_char_ratio:0,uppercase_ratio:0,average_word_length:0,score:0};let r=0;for(const o of i)INSTRUCTION_WORDS.has(o.toLowerCase().replace(/[^a-z']/g,""))&&r++;const s=r/i.length,a=t.replace(/[a-zA-Z0-9\s]/g,"").length/t.length,n=t.replace(/[^A-Z]/g,"").length,e=t.replace(/[^a-zA-Z]/g,"").length,l=e>0?n/e:0,h=i.reduce((o,_)=>o+_.length,0)/i.length;let d=0;return s>.15&&(d+=.1),s>.25&&(d+=.1),l>.4&&e>20&&(d+=.05),{instruction_word_density:s,special_char_ratio:a,uppercase_ratio:l,average_word_length:h,score:d}}}exports.HeuristicAnalyzer=HeuristicAnalyzer;
|