circle-ir-ai 2.48.0 → 2.55.0
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 +249 -0
- package/dist/trust/index.d.ts +7 -2
- package/dist/trust/index.d.ts.map +1 -1
- package/dist/trust/index.js +9 -0
- package/dist/trust/index.js.map +1 -1
- package/dist/trust/passes/agent-inventory.d.ts.map +1 -1
- package/dist/trust/passes/agent-inventory.js +3 -0
- package/dist/trust/passes/agent-inventory.js.map +1 -1
- package/dist/trust/passes/ai-config-detect.d.ts +45 -0
- package/dist/trust/passes/ai-config-detect.d.ts.map +1 -0
- package/dist/trust/passes/ai-config-detect.js +116 -0
- package/dist/trust/passes/ai-config-detect.js.map +1 -0
- package/dist/trust/passes/crypto-weakness.d.ts +30 -0
- package/dist/trust/passes/crypto-weakness.d.ts.map +1 -0
- package/dist/trust/passes/crypto-weakness.js +68 -0
- package/dist/trust/passes/crypto-weakness.js.map +1 -0
- package/dist/trust/passes/dep-manifests.d.ts +25 -0
- package/dist/trust/passes/dep-manifests.d.ts.map +1 -0
- package/dist/trust/passes/dep-manifests.js +190 -0
- package/dist/trust/passes/dep-manifests.js.map +1 -0
- package/dist/trust/passes/dependency-audit.d.ts.map +1 -1
- package/dist/trust/passes/dependency-audit.js +4 -0
- package/dist/trust/passes/dependency-audit.js.map +1 -1
- package/dist/trust/passes/dependency-staleness.d.ts.map +1 -1
- package/dist/trust/passes/dependency-staleness.js +3 -0
- package/dist/trust/passes/dependency-staleness.js.map +1 -1
- package/dist/trust/passes/eu-ai-act-flags.d.ts.map +1 -1
- package/dist/trust/passes/eu-ai-act-flags.js +5 -0
- package/dist/trust/passes/eu-ai-act-flags.js.map +1 -1
- package/dist/trust/passes/excessive-permissions.d.ts.map +1 -1
- package/dist/trust/passes/excessive-permissions.js +26 -0
- package/dist/trust/passes/excessive-permissions.js.map +1 -1
- package/dist/trust/passes/license-compliance.d.ts.map +1 -1
- package/dist/trust/passes/license-compliance.js +4 -1
- package/dist/trust/passes/license-compliance.js.map +1 -1
- package/dist/trust/passes/malicious-install-script.d.ts.map +1 -1
- package/dist/trust/passes/malicious-install-script.js +3 -1
- package/dist/trust/passes/malicious-install-script.js.map +1 -1
- package/dist/trust/passes/osv-dependency-audit.d.ts +27 -0
- package/dist/trust/passes/osv-dependency-audit.d.ts.map +1 -0
- package/dist/trust/passes/osv-dependency-audit.js +156 -0
- package/dist/trust/passes/osv-dependency-audit.js.map +1 -0
- package/dist/trust/passes/prompt-injection.d.ts.map +1 -1
- package/dist/trust/passes/prompt-injection.js +12 -0
- package/dist/trust/passes/prompt-injection.js.map +1 -1
- package/dist/trust/passes/tool-poisoning.d.ts.map +1 -1
- package/dist/trust/passes/tool-poisoning.js +21 -1
- package/dist/trust/passes/tool-poisoning.js.map +1 -1
- package/dist/trust/passes/typosquat-detection.d.ts.map +1 -1
- package/dist/trust/passes/typosquat-detection.js +3 -0
- package/dist/trust/passes/typosquat-detection.js.map +1 -1
- package/dist/trust/reachability.d.ts +37 -0
- package/dist/trust/reachability.d.ts.map +1 -0
- package/dist/trust/reachability.js +63 -0
- package/dist/trust/reachability.js.map +1 -0
- package/dist/trust/scoring.d.ts +11 -1
- package/dist/trust/scoring.d.ts.map +1 -1
- package/dist/trust/scoring.js +99 -23
- package/dist/trust/scoring.js.map +1 -1
- package/dist/trust/security-score.d.ts +48 -0
- package/dist/trust/security-score.d.ts.map +1 -0
- package/dist/trust/security-score.js +60 -0
- package/dist/trust/security-score.js.map +1 -0
- package/dist/trust/types.d.ts +67 -0
- package/dist/trust/types.d.ts.map +1 -1
- package/dist/trust/types.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* cognium-ai#234 (L4) — reachability factor.
|
|
3
|
+
*
|
|
4
|
+
* `reachability_factor` shipped as a hardcoded `1.0` since the trust score's
|
|
5
|
+
* inception (T9 vuln-reachability was deferred as "complex call-graph
|
|
6
|
+
* analysis"); the #234 critical review flagged it as a dead leg. circle-ir now
|
|
7
|
+
* emits taint flows during `analyze()`, so reachability is cheap to measure:
|
|
8
|
+
*
|
|
9
|
+
* A dangerous **sink** that terminates at least one *unsanitized* taint
|
|
10
|
+
* **flow** from an untrusted source is *confirmed reachable* — a real,
|
|
11
|
+
* exploitable path, not a dormant pattern. A sink with no flow reaching it is
|
|
12
|
+
* not confirmed reachable. Two repos with identical sink patterns are not
|
|
13
|
+
* equally risky: the one where untrusted input actually reaches those sinks is
|
|
14
|
+
* worse, and the composite should reflect that.
|
|
15
|
+
*
|
|
16
|
+
* The factor is a bounded penalty multiplier on the composite:
|
|
17
|
+
*
|
|
18
|
+
* factor = 1 - MAX_PENALTY × (reachable dangerous sinks / all dangerous sinks)
|
|
19
|
+
*
|
|
20
|
+
* so a repo where every dangerous sink is reachable-and-unsanitized takes the
|
|
21
|
+
* full `MAX_PENALTY` haircut, and one where sinks exist but nothing reaches
|
|
22
|
+
* them stays neutral at 1.0. `measured: false` (⇒ neutral 1.0) when circle-ir
|
|
23
|
+
* did not run or there are no dangerous sinks — coverage-honest, never a fake
|
|
24
|
+
* penalty for something we couldn't see.
|
|
25
|
+
*/
|
|
26
|
+
import type { CircleIR } from 'circle-ir';
|
|
27
|
+
import type { ReachabilityDetail } from './types.js';
|
|
28
|
+
/** Max fraction the composite can be reduced by reachability (15% haircut). */
|
|
29
|
+
export declare const REACHABILITY_MAX_PENALTY = 0.15;
|
|
30
|
+
export type { ReachabilityDetail };
|
|
31
|
+
/**
|
|
32
|
+
* Compute the reachability factor from circle-ir results. Neutral (1.0,
|
|
33
|
+
* `measured:false`) when `circleIRResults` is absent/empty or no file has a
|
|
34
|
+
* dangerous sink.
|
|
35
|
+
*/
|
|
36
|
+
export declare function computeReachabilityFactor(circleIRResults?: CircleIR[]): ReachabilityDetail;
|
|
37
|
+
//# sourceMappingURL=reachability.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reachability.d.ts","sourceRoot":"","sources":["../../src/trust/reachability.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAErD,+EAA+E;AAC/E,eAAO,MAAM,wBAAwB,OAAO,CAAC;AAE7C,YAAY,EAAE,kBAAkB,EAAE,CAAC;AAoBnC;;;;GAIG;AACH,wBAAgB,yBAAyB,CACvC,eAAe,CAAC,EAAE,QAAQ,EAAE,GAC3B,kBAAkB,CA8CpB"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/** Max fraction the composite can be reduced by reachability (15% haircut). */
|
|
2
|
+
export const REACHABILITY_MAX_PENALTY = 0.15;
|
|
3
|
+
const NEUTRAL = (over = {}) => ({
|
|
4
|
+
factor: 1.0,
|
|
5
|
+
measured: false,
|
|
6
|
+
dangerousSinks: 0,
|
|
7
|
+
reachableSinks: 0,
|
|
8
|
+
unsanitizedFlows: 0,
|
|
9
|
+
reachableRatio: 0,
|
|
10
|
+
maxPenalty: REACHABILITY_MAX_PENALTY,
|
|
11
|
+
...over,
|
|
12
|
+
});
|
|
13
|
+
const sinkKey = (line, type) => `${line ?? -1}:${type ?? ''}`;
|
|
14
|
+
/**
|
|
15
|
+
* Compute the reachability factor from circle-ir results. Neutral (1.0,
|
|
16
|
+
* `measured:false`) when `circleIRResults` is absent/empty or no file has a
|
|
17
|
+
* dangerous sink.
|
|
18
|
+
*/
|
|
19
|
+
export function computeReachabilityFactor(circleIRResults) {
|
|
20
|
+
if (!circleIRResults || circleIRResults.length === 0)
|
|
21
|
+
return NEUTRAL();
|
|
22
|
+
let dangerousSinks = 0;
|
|
23
|
+
let reachableSinks = 0;
|
|
24
|
+
let unsanitizedFlows = 0;
|
|
25
|
+
for (const ir of circleIRResults) {
|
|
26
|
+
const taint = ir.taint;
|
|
27
|
+
const sinks = taint?.sinks ?? [];
|
|
28
|
+
const flows = taint?.flows ?? [];
|
|
29
|
+
dangerousSinks += sinks.length;
|
|
30
|
+
// Keys of sinks reached by an unsanitized flow (per-file, so line numbers
|
|
31
|
+
// don't collide across files). `sanitized !== true` is conservative:
|
|
32
|
+
// an undefined/absent flag counts as unsanitized.
|
|
33
|
+
const reachedKeys = new Set();
|
|
34
|
+
for (const f of flows) {
|
|
35
|
+
if (f.sanitized === true)
|
|
36
|
+
continue;
|
|
37
|
+
unsanitizedFlows++;
|
|
38
|
+
reachedKeys.add(sinkKey(f.sink_line, f.sink_type));
|
|
39
|
+
}
|
|
40
|
+
if (reachedKeys.size === 0)
|
|
41
|
+
continue;
|
|
42
|
+
for (const s of sinks) {
|
|
43
|
+
if (reachedKeys.has(sinkKey(s.line, s.type)))
|
|
44
|
+
reachableSinks++;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
// No dangerous sinks anywhere → nothing to be reachable → neutral.
|
|
48
|
+
if (dangerousSinks === 0) {
|
|
49
|
+
return NEUTRAL({ measured: false, unsanitizedFlows });
|
|
50
|
+
}
|
|
51
|
+
const reachableRatio = Math.min(1, reachableSinks / dangerousSinks);
|
|
52
|
+
const factor = Math.round((1 - REACHABILITY_MAX_PENALTY * reachableRatio) * 1000) / 1000;
|
|
53
|
+
return {
|
|
54
|
+
factor,
|
|
55
|
+
measured: true,
|
|
56
|
+
dangerousSinks,
|
|
57
|
+
reachableSinks,
|
|
58
|
+
unsanitizedFlows,
|
|
59
|
+
reachableRatio: Math.round(reachableRatio * 1000) / 1000,
|
|
60
|
+
maxPenalty: REACHABILITY_MAX_PENALTY,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=reachability.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reachability.js","sourceRoot":"","sources":["../../src/trust/reachability.ts"],"names":[],"mappings":"AA4BA,+EAA+E;AAC/E,MAAM,CAAC,MAAM,wBAAwB,GAAG,IAAI,CAAC;AAQ7C,MAAM,OAAO,GAAG,CAAC,OAAoC,EAAE,EAAsB,EAAE,CAAC,CAAC;IAC/E,MAAM,EAAE,GAAG;IACX,QAAQ,EAAE,KAAK;IACf,cAAc,EAAE,CAAC;IACjB,cAAc,EAAE,CAAC;IACjB,gBAAgB,EAAE,CAAC;IACnB,cAAc,EAAE,CAAC;IACjB,UAAU,EAAE,wBAAwB;IACpC,GAAG,IAAI;CACR,CAAC,CAAC;AAEH,MAAM,OAAO,GAAG,CAAC,IAAwB,EAAE,IAAwB,EAAU,EAAE,CAC7E,GAAG,IAAI,IAAI,CAAC,CAAC,IAAI,IAAI,IAAI,EAAE,EAAE,CAAC;AAEhC;;;;GAIG;AACH,MAAM,UAAU,yBAAyB,CACvC,eAA4B;IAE5B,IAAI,CAAC,eAAe,IAAI,eAAe,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,OAAO,EAAE,CAAC;IAEvE,IAAI,cAAc,GAAG,CAAC,CAAC;IACvB,IAAI,cAAc,GAAG,CAAC,CAAC;IACvB,IAAI,gBAAgB,GAAG,CAAC,CAAC;IAEzB,KAAK,MAAM,EAAE,IAAI,eAAe,EAAE,CAAC;QACjC,MAAM,KAAK,GAAI,EAAkF,CAAC,KAAK,CAAC;QACxG,MAAM,KAAK,GAAG,KAAK,EAAE,KAAK,IAAI,EAAE,CAAC;QACjC,MAAM,KAAK,GAAG,KAAK,EAAE,KAAK,IAAI,EAAE,CAAC;QACjC,cAAc,IAAI,KAAK,CAAC,MAAM,CAAC;QAE/B,0EAA0E;QAC1E,qEAAqE;QACrE,kDAAkD;QAClD,MAAM,WAAW,GAAG,IAAI,GAAG,EAAU,CAAC;QACtC,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;YACtB,IAAI,CAAC,CAAC,SAAS,KAAK,IAAI;gBAAE,SAAS;YACnC,gBAAgB,EAAE,CAAC;YACnB,WAAW,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC;QACrD,CAAC;QACD,IAAI,WAAW,CAAC,IAAI,KAAK,CAAC;YAAE,SAAS;QAErC,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;YACtB,IAAI,WAAW,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;gBAAE,cAAc,EAAE,CAAC;QACjE,CAAC;IACH,CAAC;IAED,mEAAmE;IACnE,IAAI,cAAc,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,OAAO,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,gBAAgB,EAAE,CAAC,CAAC;IACxD,CAAC;IAED,MAAM,cAAc,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,cAAc,GAAG,cAAc,CAAC,CAAC;IACpE,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,wBAAwB,GAAG,cAAc,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC;IAEzF,OAAO;QACL,MAAM;QACN,QAAQ,EAAE,IAAI;QACd,cAAc;QACd,cAAc;QACd,gBAAgB;QAChB,cAAc,EAAE,IAAI,CAAC,KAAK,CAAC,cAAc,GAAG,IAAI,CAAC,GAAG,IAAI;QACxD,UAAU,EAAE,wBAAwB;KACrC,CAAC;AACJ,CAAC"}
|
package/dist/trust/scoring.d.ts
CHANGED
|
@@ -5,7 +5,17 @@
|
|
|
5
5
|
* Each pass maps to a category with a configurable weight, and to one
|
|
6
6
|
* of the 13 TrustScoreResult component fields.
|
|
7
7
|
*/
|
|
8
|
+
import type { CircleIR } from 'circle-ir';
|
|
8
9
|
import { type TrustPassResult, type TrustScoreResult } from './types.js';
|
|
10
|
+
/**
|
|
11
|
+
* cognium-ai#234 (L4) — optional analysis context for the scoring engine.
|
|
12
|
+
* Carries circle-ir results so the reachability factor can be measured. Absent
|
|
13
|
+
* ⇒ neutral factor 1.0 (byte-identical to pre-L4 scoring for every caller that
|
|
14
|
+
* doesn't pass it).
|
|
15
|
+
*/
|
|
16
|
+
export interface TrustScoringContext {
|
|
17
|
+
circleIRResults?: CircleIR[];
|
|
18
|
+
}
|
|
9
19
|
export type TrustCategory = 'supply_chain' | 'code_security' | 'ai_safety' | 'compliance' | 'artifacts';
|
|
10
20
|
export interface TrustScoringConfig {
|
|
11
21
|
weights: Record<TrustCategory, number>;
|
|
@@ -40,6 +50,6 @@ export declare function validateScoringConfig(config: TrustScoringConfig): void;
|
|
|
40
50
|
* 4. Populate 13 component scores via {@link PASS_COMPONENT_MAP}.
|
|
41
51
|
* 5. Derive tier via {@link getTierForScore}; check for critical findings.
|
|
42
52
|
*/
|
|
43
|
-
export declare function computeTrustScore(passResults: TrustPassResult[], config?: TrustScoringConfig): TrustScoreResult;
|
|
53
|
+
export declare function computeTrustScore(passResults: TrustPassResult[], config?: TrustScoringConfig, context?: TrustScoringContext): TrustScoreResult;
|
|
44
54
|
export {};
|
|
45
55
|
//# sourceMappingURL=scoring.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scoring.d.ts","sourceRoot":"","sources":["../../src/trust/scoring.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EACL,KAAK,eAAe,EACpB,KAAK,gBAAgB,
|
|
1
|
+
{"version":3,"file":"scoring.d.ts","sourceRoot":"","sources":["../../src/trust/scoring.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EACL,KAAK,eAAe,EACpB,KAAK,gBAAgB,EAKtB,MAAM,YAAY,CAAC;AAGpB;;;;;GAKG;AACH,MAAM,WAAW,mBAAmB;IAClC,eAAe,CAAC,EAAE,QAAQ,EAAE,CAAC;CAC9B;AAMD,MAAM,MAAM,aAAa,GACrB,cAAc,GACd,eAAe,GACf,WAAW,GACX,YAAY,GACZ,WAAW,CAAC;AAMhB,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,MAAM,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC;CACxC;AAED,eAAO,MAAM,sBAAsB,EAAE,kBAQpC,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,0BAA0B,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAE7D,CAAC;AAUF,eAAO,MAAM,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CA0E3D,CAAC;AAMF,iEAAiE;AACjE,KAAK,cAAc,GACf,qBAAqB,GACrB,oBAAoB,GACpB,oBAAoB,GACpB,mBAAmB,GACnB,kBAAkB,GAClB,eAAe,GACf,eAAe,GACf,eAAe,GACf,kBAAkB,GAClB,cAAc,GACd,iBAAiB,GACjB,kBAAkB,GAClB,mBAAmB,CAAC;AAExB,eAAO,MAAM,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAsC7D,CAAC;AAMF;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,kBAAkB,GAAG,IAAI,CAUtE;AAMD;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAC/B,WAAW,EAAE,eAAe,EAAE,EAC9B,MAAM,GAAE,kBAA2C,EACnD,OAAO,GAAE,mBAAwB,GAChC,gBAAgB,CAgMlB"}
|
package/dist/trust/scoring.js
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* of the 13 TrustScoreResult component fields.
|
|
7
7
|
*/
|
|
8
8
|
import { getTierForScore, } from './types.js';
|
|
9
|
+
import { computeReachabilityFactor } from './reachability.js';
|
|
9
10
|
export const DEFAULT_SCORING_CONFIG = {
|
|
10
11
|
weights: {
|
|
11
12
|
supply_chain: 0.3,
|
|
@@ -40,6 +41,9 @@ export const PASS_CATEGORY_MAP = {
|
|
|
40
41
|
'typosquat-detection': 'supply_chain',
|
|
41
42
|
'dependency-staleness': 'supply_chain',
|
|
42
43
|
'dependency-audit': 'supply_chain',
|
|
44
|
+
// cognium-ai#234 (L2) — ecosystem-agnostic CVE audit (Maven/PyPI/Go/Cargo)
|
|
45
|
+
// via OSV.dev; makes supply_chain a real signal on non-npm repos.
|
|
46
|
+
'osv-dependency-audit': 'supply_chain',
|
|
43
47
|
// code_security
|
|
44
48
|
'data-exfiltration': 'code_security',
|
|
45
49
|
'input-validation': 'code_security',
|
|
@@ -50,13 +54,25 @@ export const PASS_CATEGORY_MAP = {
|
|
|
50
54
|
// skipped it entirely and the aggregate ignored LLM-discovered vulns.
|
|
51
55
|
// Mapped to code_security + weighted (see PASS_INTRA_CATEGORY_WEIGHT).
|
|
52
56
|
'llm-security-scan': 'code_security',
|
|
53
|
-
//
|
|
57
|
+
// cognium-ai#234 (L3) — cryptographic-weakness pass (weak hash/cipher/PRNG/
|
|
58
|
+
// key), consuming circle-ir's crypto SAST findings.
|
|
59
|
+
'crypto-weakness': 'code_security',
|
|
60
|
+
// ai_safety — AI-agent/skill THREAT passes (all AI-surface-gated, so on a
|
|
61
|
+
// non-AI repo they're all applicable:false and L1 drops the category +
|
|
62
|
+
// re-normalizes its 20% away — cognium-ai#234 L6).
|
|
54
63
|
'prompt-injection': 'ai_safety',
|
|
55
64
|
'excessive-permissions': 'ai_safety',
|
|
56
65
|
'agent-inventory': 'ai_safety',
|
|
57
66
|
'eu-ai-act-flags': 'ai_safety',
|
|
58
67
|
'tool-poisoning': 'ai_safety',
|
|
59
|
-
|
|
68
|
+
// cognium-ai#234 (L6) — ai-generated-fingerprint measures code *provenance*
|
|
69
|
+
// (was this AI-written), not an AI-safety threat. It runs on any source and
|
|
70
|
+
// is legitimately clean-100 on human-written code, so leaving it in ai_safety
|
|
71
|
+
// kept that 20% category permanently "validated" on non-AI repos — exactly
|
|
72
|
+
// the constant the #234 review flagged. Moved to artifacts (informational,
|
|
73
|
+
// weight 0): still reported (provenance_score + findings), no longer anchors
|
|
74
|
+
// a security category. It CANNOT be gated on findings — "no AI patterns" is a
|
|
75
|
+
// real clean result, not "couldn't look".
|
|
60
76
|
// compliance
|
|
61
77
|
'license-compliance': 'compliance',
|
|
62
78
|
'malicious-install': 'compliance',
|
|
@@ -79,16 +95,18 @@ export const PASS_CATEGORY_MAP = {
|
|
|
79
95
|
'naming-convention': 'code_security',
|
|
80
96
|
// code_security (Sprint 10 — Semantic Understanding)
|
|
81
97
|
'spec-gap-report': 'code_security',
|
|
82
|
-
// artifacts
|
|
98
|
+
// artifacts (informational, weight 0)
|
|
83
99
|
'sbom-generation': 'artifacts',
|
|
84
100
|
'vex-generation': 'artifacts',
|
|
85
101
|
'audit-trail': 'artifacts',
|
|
102
|
+
'ai-generated-fingerprint': 'artifacts', // #234 L6 — provenance, not a threat
|
|
86
103
|
};
|
|
87
104
|
export const PASS_COMPONENT_MAP = {
|
|
88
105
|
'input-validation': 'vulnerability_score',
|
|
89
106
|
'typosquat-detection': 'supply_chain_score',
|
|
90
107
|
'dependency-staleness': 'supply_chain_score',
|
|
91
108
|
'dependency-audit': 'supply_chain_score',
|
|
109
|
+
'osv-dependency-audit': 'supply_chain_score',
|
|
92
110
|
'data-exfiltration': 'exfiltration_score',
|
|
93
111
|
'prompt-injection': 'instruction_score',
|
|
94
112
|
'excessive-permissions': 'permission_score',
|
|
@@ -118,7 +136,9 @@ export const PASS_COMPONENT_MAP = {
|
|
|
118
136
|
'god-class': 'hygiene_score',
|
|
119
137
|
'naming-convention': 'hygiene_score',
|
|
120
138
|
'spec-gap-report': 'hygiene_score',
|
|
121
|
-
// crypto_score
|
|
139
|
+
// cognium-ai#234 (L3) — crypto_score is now fed by the crypto-weakness pass
|
|
140
|
+
// (was a permanent 100 with no pass mapped here).
|
|
141
|
+
'crypto-weakness': 'crypto_score',
|
|
122
142
|
};
|
|
123
143
|
// ---------------------------------------------------------------------------
|
|
124
144
|
// Validation
|
|
@@ -147,19 +167,31 @@ export function validateScoringConfig(config) {
|
|
|
147
167
|
* 4. Populate 13 component scores via {@link PASS_COMPONENT_MAP}.
|
|
148
168
|
* 5. Derive tier via {@link getTierForScore}; check for critical findings.
|
|
149
169
|
*/
|
|
150
|
-
export function computeTrustScore(passResults, config = DEFAULT_SCORING_CONFIG) {
|
|
170
|
+
export function computeTrustScore(passResults, config = DEFAULT_SCORING_CONFIG, context = {}) {
|
|
151
171
|
validateScoringConfig(config);
|
|
152
|
-
// -- Step 1 & 2: Category averages
|
|
172
|
+
// -- Step 1 & 2: Category averages (cognium-ai#234 L1 — coverage-aware) ----
|
|
153
173
|
// cognium-ai#119 — weighted accumulation per category (weight from
|
|
154
|
-
// PASS_INTRA_CATEGORY_WEIGHT, default 1).
|
|
155
|
-
//
|
|
174
|
+
// PASS_INTRA_CATEGORY_WEIGHT, default 1).
|
|
175
|
+
// cognium-ai#234 (L1) — a pass with `applicable === false` couldn't look
|
|
176
|
+
// (wrong ecosystem, no relevant files), so its 100 is NOT evidence of
|
|
177
|
+
// cleanliness — exclude it from its category average, and remember it as
|
|
178
|
+
// inapplicable for the coverage report.
|
|
156
179
|
const categoryScores = new Map();
|
|
180
|
+
const inapplicableByCategory = new Map();
|
|
157
181
|
for (const pr of passResults) {
|
|
158
182
|
const category = PASS_CATEGORY_MAP[pr.pass];
|
|
159
183
|
if (category === undefined) {
|
|
160
184
|
// Unknown pass — skip silently so future passes don't break scoring
|
|
161
185
|
continue;
|
|
162
186
|
}
|
|
187
|
+
if (pr.applicable === false) {
|
|
188
|
+
const list = inapplicableByCategory.get(category);
|
|
189
|
+
if (list)
|
|
190
|
+
list.push(pr.pass);
|
|
191
|
+
else
|
|
192
|
+
inapplicableByCategory.set(category, [pr.pass]);
|
|
193
|
+
continue;
|
|
194
|
+
}
|
|
163
195
|
const w = passWeight(pr.pass);
|
|
164
196
|
const bucket = categoryScores.get(category);
|
|
165
197
|
if (bucket) {
|
|
@@ -170,32 +202,71 @@ export function computeTrustScore(passResults, config = DEFAULT_SCORING_CONFIG)
|
|
|
170
202
|
categoryScores.set(category, { weightedSum: pr.score * w, weightSum: w });
|
|
171
203
|
}
|
|
172
204
|
}
|
|
205
|
+
// A category is VALIDATED only if ≥1 applicable pass contributed.
|
|
173
206
|
const categoryAverages = new Map();
|
|
174
207
|
for (const [cat, { weightedSum, weightSum }] of categoryScores) {
|
|
175
|
-
|
|
208
|
+
if (weightSum > 0)
|
|
209
|
+
categoryAverages.set(cat, weightedSum / weightSum);
|
|
176
210
|
}
|
|
177
|
-
// -- Step 3:
|
|
178
|
-
|
|
179
|
-
|
|
211
|
+
// -- Step 3: Coverage-aware weighted composite ----------------------------
|
|
212
|
+
//
|
|
213
|
+
// cognium-ai#234 (L1) — the composite is the weighted mean of ONLY the
|
|
214
|
+
// validated categories, with weights RE-NORMALIZED across them. An
|
|
215
|
+
// unvalidated category is excluded rather than treated as a clean 100, so a
|
|
216
|
+
// score can no longer be inflated by legs that never looked (npm-only
|
|
217
|
+
// dependency audit on a Maven repo, AI-safety on a non-agent repo, …). The
|
|
218
|
+
// `coverageWeight` (sum of validated default weights) tells consumers how
|
|
219
|
+
// much of the score rests on real signal.
|
|
220
|
+
const scoredCategories = [
|
|
180
221
|
'supply_chain',
|
|
181
222
|
'code_security',
|
|
182
223
|
'ai_safety',
|
|
183
224
|
'compliance',
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
225
|
+
]; // artifacts is weight 0 / informational — never in the composite
|
|
226
|
+
let weightedSum = 0;
|
|
227
|
+
let validatedWeight = 0;
|
|
228
|
+
for (const cat of scoredCategories) {
|
|
187
229
|
const weight = config.weights[cat];
|
|
188
230
|
if (weight === 0)
|
|
189
231
|
continue;
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
232
|
+
const avg = categoryAverages.get(cat);
|
|
233
|
+
if (avg === undefined)
|
|
234
|
+
continue; // unvalidated — excluded + re-normalized
|
|
235
|
+
weightedSum += avg * weight;
|
|
236
|
+
validatedWeight += weight;
|
|
193
237
|
}
|
|
194
|
-
//
|
|
238
|
+
// Re-normalize over validated weight. If NOTHING validated (pathological —
|
|
239
|
+
// no pass applied at all), fall back to 100 but flag coverageWeight 0.
|
|
240
|
+
let compositeScore = validatedWeight > 0 ? weightedSum / validatedWeight : 100;
|
|
195
241
|
compositeScore = Math.max(0, Math.min(100, Math.round(compositeScore * 100) / 100));
|
|
242
|
+
// cognium-ai#234 (L4) — apply the reachability factor. A dangerous sink that
|
|
243
|
+
// an unsanitized taint flow actually reaches is a confirmed exploitable path;
|
|
244
|
+
// the composite is discounted in proportion to how much of the sink surface
|
|
245
|
+
// is reachable. Neutral 1.0 (no change) when circle-ir didn't run or there
|
|
246
|
+
// are no dangerous sinks — so every caller that omits `context` is
|
|
247
|
+
// byte-identical to pre-L4 scoring.
|
|
248
|
+
const reachability = computeReachabilityFactor(context.circleIRResults);
|
|
249
|
+
compositeScore = Math.max(0, Math.min(100, Math.round(compositeScore * reachability.factor * 100) / 100));
|
|
250
|
+
// Coverage report (additive output).
|
|
251
|
+
const categoryCoverage = scoredCategories.map((cat) => {
|
|
252
|
+
const avg = categoryAverages.get(cat);
|
|
253
|
+
return {
|
|
254
|
+
category: cat,
|
|
255
|
+
validated: avg !== undefined,
|
|
256
|
+
score: avg !== undefined ? Math.round(avg * 100) / 100 : null,
|
|
257
|
+
inapplicablePasses: inapplicableByCategory.get(cat) ?? [],
|
|
258
|
+
weight: config.weights[cat],
|
|
259
|
+
};
|
|
260
|
+
});
|
|
261
|
+
const coverageWeight = Math.round(validatedWeight * 1000) / 1000;
|
|
196
262
|
// -- Step 4: Component scores ---------------------------------------------
|
|
197
263
|
const componentBuckets = new Map();
|
|
198
264
|
for (const pr of passResults) {
|
|
265
|
+
// cognium-ai#234 (L1) — an inapplicable pass never looked; excluding it
|
|
266
|
+
// here keeps the display component honest too (a bucket shared by an
|
|
267
|
+
// applicable low score and an inapplicable 100 must not be inflated).
|
|
268
|
+
if (pr.applicable === false)
|
|
269
|
+
continue;
|
|
199
270
|
const component = PASS_COMPONENT_MAP[pr.pass];
|
|
200
271
|
if (component === undefined)
|
|
201
272
|
continue;
|
|
@@ -210,7 +281,7 @@ export function computeTrustScore(passResults, config = DEFAULT_SCORING_CONFIG)
|
|
|
210
281
|
const componentAvg = (key) => {
|
|
211
282
|
const scores = componentBuckets.get(key);
|
|
212
283
|
if (!scores || scores.length === 0)
|
|
213
|
-
return 100; // default clean
|
|
284
|
+
return 100; // default clean / unvalidated
|
|
214
285
|
return scores.reduce((s, v) => s + v, 0) / scores.length;
|
|
215
286
|
};
|
|
216
287
|
// -- Step 5: Critical findings → BLOCKED ----------------------------------
|
|
@@ -246,6 +317,9 @@ export function computeTrustScore(passResults, config = DEFAULT_SCORING_CONFIG)
|
|
|
246
317
|
tierReason,
|
|
247
318
|
baseTier,
|
|
248
319
|
blockingFindings,
|
|
320
|
+
// cognium-ai#234 (L1) — coverage transparency
|
|
321
|
+
categoryCoverage,
|
|
322
|
+
coverageWeight,
|
|
249
323
|
// 12 component scores
|
|
250
324
|
vulnerability_score: componentAvg('vulnerability_score'),
|
|
251
325
|
supply_chain_score: componentAvg('supply_chain_score'),
|
|
@@ -253,17 +327,19 @@ export function computeTrustScore(passResults, config = DEFAULT_SCORING_CONFIG)
|
|
|
253
327
|
instruction_score: componentAvg('instruction_score'),
|
|
254
328
|
permission_score: componentAvg('permission_score'),
|
|
255
329
|
secrets_score: componentAvg('secrets_score'),
|
|
256
|
-
crypto_score: componentAvg('crypto_score'), //
|
|
330
|
+
crypto_score: componentAvg('crypto_score'), // #234 L3 → crypto-weakness pass
|
|
257
331
|
license_score: componentAvg('license_score'),
|
|
258
332
|
compliance_score: componentAvg('compliance_score'),
|
|
259
333
|
hygiene_score: componentAvg('hygiene_score'),
|
|
260
334
|
poisoning_score: componentAvg('poisoning_score'),
|
|
261
335
|
provenance_score: componentAvg('provenance_score'),
|
|
262
336
|
performance_score: componentAvg('performance_score'),
|
|
263
|
-
// 3 multiplicative factors
|
|
264
|
-
|
|
337
|
+
// 3 multiplicative factors. reachability is LIVE (#234 L4); drift +
|
|
338
|
+
// freshness remain neutral pending L-series follow-ups.
|
|
339
|
+
reachability_factor: reachability.factor,
|
|
265
340
|
drift_factor: 1.0,
|
|
266
341
|
freshness_factor: 1.0,
|
|
342
|
+
reachabilityDetail: reachability,
|
|
267
343
|
passResults,
|
|
268
344
|
timestamp: new Date().toISOString(),
|
|
269
345
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scoring.js","sourceRoot":"","sources":["../../src/trust/scoring.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;
|
|
1
|
+
{"version":3,"file":"scoring.js","sourceRoot":"","sources":["../../src/trust/scoring.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* cognium-ai#234 (L5) — de-saturate code_security.
|
|
3
|
+
*
|
|
4
|
+
* The synthetic `llm-security-scan` pass (weight 10 in code_security) scored
|
|
5
|
+
* findings with a linear penalty clamped to [0,100]:
|
|
6
|
+
*
|
|
7
|
+
* score = 100 - (crit×25 + high×10 + med×3 + low×1)
|
|
8
|
+
*
|
|
9
|
+
* That pins to 0 the moment the load crosses 100 — i.e. ~4 criticals. On a
|
|
10
|
+
* large repo (the #234 review saw 28,300 raw findings) 3 vulns and 3,000 both
|
|
11
|
+
* score ≈0: the pass carries a heavy weight but conveys no signal beyond
|
|
12
|
+
* "something fired". It saturates instead of discriminating.
|
|
13
|
+
*
|
|
14
|
+
* This replaces the linear clamp with a **rational (reciprocal) decay** on the
|
|
15
|
+
* same severity-weighted load:
|
|
16
|
+
*
|
|
17
|
+
* score = 100 × SCALE / (SCALE + load)
|
|
18
|
+
*
|
|
19
|
+
* Properties: 100 at load 0; 50 at load = SCALE; monotonic decreasing;
|
|
20
|
+
* asymptotic to 0 but never pinned there, so it keeps ranking across the whole
|
|
21
|
+
* range (3 findings ≠ 3,000). The severity weights are unchanged, so the
|
|
22
|
+
* ordering of "which repo is worse" is preserved — only the saturation is
|
|
23
|
+
* removed. Numeric leniency at moderate counts is intentional and safe: the
|
|
24
|
+
* BLOCKED tier override still fires on ANY critical finding regardless of this
|
|
25
|
+
* number, so a repo with real criticals cannot pass on score alone.
|
|
26
|
+
*/
|
|
27
|
+
/** Severity → penalty weight (unchanged from the legacy linear formula). */
|
|
28
|
+
export declare const SECURITY_SEVERITY_PENALTY: Record<'critical' | 'high' | 'medium' | 'low', number>;
|
|
29
|
+
/**
|
|
30
|
+
* Half-life load: the severity-weighted load at which the score is 50.
|
|
31
|
+
* 90 ≈ 3–4 criticals, ~9 highs, or ~30 mediums. Tunable.
|
|
32
|
+
*/
|
|
33
|
+
export declare const SECURITY_SCORE_SCALE = 90;
|
|
34
|
+
export interface SecurityFindingCounts {
|
|
35
|
+
critical?: number;
|
|
36
|
+
high?: number;
|
|
37
|
+
medium?: number;
|
|
38
|
+
low?: number;
|
|
39
|
+
}
|
|
40
|
+
/** Severity-weighted load (higher = worse). */
|
|
41
|
+
export declare function securityLoad(counts: SecurityFindingCounts): number;
|
|
42
|
+
/**
|
|
43
|
+
* Discriminating 0–100 security score via rational decay. Never pins to 0, so
|
|
44
|
+
* a handful of findings and thousands remain distinguishable. `scale` is the
|
|
45
|
+
* load at which the score is 50 (defaults to {@link SECURITY_SCORE_SCALE}).
|
|
46
|
+
*/
|
|
47
|
+
export declare function discriminatingSecurityScore(counts: SecurityFindingCounts, scale?: number): number;
|
|
48
|
+
//# sourceMappingURL=security-score.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"security-score.d.ts","sourceRoot":"","sources":["../../src/trust/security-score.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,4EAA4E;AAC5E,eAAO,MAAM,yBAAyB,EAAE,MAAM,CAAC,UAAU,GAAG,MAAM,GAAG,QAAQ,GAAG,KAAK,EAAE,MAAM,CAK5F,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,oBAAoB,KAAK,CAAC;AAEvC,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,+CAA+C;AAC/C,wBAAgB,YAAY,CAAC,MAAM,EAAE,qBAAqB,GAAG,MAAM,CAOlE;AAED;;;;GAIG;AACH,wBAAgB,2BAA2B,CACzC,MAAM,EAAE,qBAAqB,EAC7B,KAAK,GAAE,MAA6B,GACnC,MAAM,CAOR"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* cognium-ai#234 (L5) — de-saturate code_security.
|
|
3
|
+
*
|
|
4
|
+
* The synthetic `llm-security-scan` pass (weight 10 in code_security) scored
|
|
5
|
+
* findings with a linear penalty clamped to [0,100]:
|
|
6
|
+
*
|
|
7
|
+
* score = 100 - (crit×25 + high×10 + med×3 + low×1)
|
|
8
|
+
*
|
|
9
|
+
* That pins to 0 the moment the load crosses 100 — i.e. ~4 criticals. On a
|
|
10
|
+
* large repo (the #234 review saw 28,300 raw findings) 3 vulns and 3,000 both
|
|
11
|
+
* score ≈0: the pass carries a heavy weight but conveys no signal beyond
|
|
12
|
+
* "something fired". It saturates instead of discriminating.
|
|
13
|
+
*
|
|
14
|
+
* This replaces the linear clamp with a **rational (reciprocal) decay** on the
|
|
15
|
+
* same severity-weighted load:
|
|
16
|
+
*
|
|
17
|
+
* score = 100 × SCALE / (SCALE + load)
|
|
18
|
+
*
|
|
19
|
+
* Properties: 100 at load 0; 50 at load = SCALE; monotonic decreasing;
|
|
20
|
+
* asymptotic to 0 but never pinned there, so it keeps ranking across the whole
|
|
21
|
+
* range (3 findings ≠ 3,000). The severity weights are unchanged, so the
|
|
22
|
+
* ordering of "which repo is worse" is preserved — only the saturation is
|
|
23
|
+
* removed. Numeric leniency at moderate counts is intentional and safe: the
|
|
24
|
+
* BLOCKED tier override still fires on ANY critical finding regardless of this
|
|
25
|
+
* number, so a repo with real criticals cannot pass on score alone.
|
|
26
|
+
*/
|
|
27
|
+
/** Severity → penalty weight (unchanged from the legacy linear formula). */
|
|
28
|
+
export const SECURITY_SEVERITY_PENALTY = {
|
|
29
|
+
critical: 25,
|
|
30
|
+
high: 10,
|
|
31
|
+
medium: 3,
|
|
32
|
+
low: 1,
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Half-life load: the severity-weighted load at which the score is 50.
|
|
36
|
+
* 90 ≈ 3–4 criticals, ~9 highs, or ~30 mediums. Tunable.
|
|
37
|
+
*/
|
|
38
|
+
export const SECURITY_SCORE_SCALE = 90;
|
|
39
|
+
/** Severity-weighted load (higher = worse). */
|
|
40
|
+
export function securityLoad(counts) {
|
|
41
|
+
return ((counts.critical ?? 0) * SECURITY_SEVERITY_PENALTY.critical +
|
|
42
|
+
(counts.high ?? 0) * SECURITY_SEVERITY_PENALTY.high +
|
|
43
|
+
(counts.medium ?? 0) * SECURITY_SEVERITY_PENALTY.medium +
|
|
44
|
+
(counts.low ?? 0) * SECURITY_SEVERITY_PENALTY.low);
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Discriminating 0–100 security score via rational decay. Never pins to 0, so
|
|
48
|
+
* a handful of findings and thousands remain distinguishable. `scale` is the
|
|
49
|
+
* load at which the score is 50 (defaults to {@link SECURITY_SCORE_SCALE}).
|
|
50
|
+
*/
|
|
51
|
+
export function discriminatingSecurityScore(counts, scale = SECURITY_SCORE_SCALE) {
|
|
52
|
+
const load = securityLoad(counts);
|
|
53
|
+
if (load <= 0)
|
|
54
|
+
return 100;
|
|
55
|
+
const s = scale > 0 ? scale : SECURITY_SCORE_SCALE;
|
|
56
|
+
const raw = (100 * s) / (s + load);
|
|
57
|
+
// 2 decimals so the high end still ranks (0.12 vs 0.06) instead of both → 0.
|
|
58
|
+
return Math.round(raw * 100) / 100;
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=security-score.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"security-score.js","sourceRoot":"","sources":["../../src/trust/security-score.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,4EAA4E;AAC5E,MAAM,CAAC,MAAM,yBAAyB,GAA2D;IAC/F,QAAQ,EAAE,EAAE;IACZ,IAAI,EAAE,EAAE;IACR,MAAM,EAAE,CAAC;IACT,GAAG,EAAE,CAAC;CACP,CAAC;AAEF;;;GAGG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,EAAE,CAAC;AASvC,+CAA+C;AAC/C,MAAM,UAAU,YAAY,CAAC,MAA6B;IACxD,OAAO,CACL,CAAC,MAAM,CAAC,QAAQ,IAAI,CAAC,CAAC,GAAG,yBAAyB,CAAC,QAAQ;QAC3D,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,CAAC,GAAG,yBAAyB,CAAC,IAAI;QACnD,CAAC,MAAM,CAAC,MAAM,IAAI,CAAC,CAAC,GAAG,yBAAyB,CAAC,MAAM;QACvD,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC,GAAG,yBAAyB,CAAC,GAAG,CAClD,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,2BAA2B,CACzC,MAA6B,EAC7B,QAAgB,oBAAoB;IAEpC,MAAM,IAAI,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;IAClC,IAAI,IAAI,IAAI,CAAC;QAAE,OAAO,GAAG,CAAC;IAC1B,MAAM,CAAC,GAAG,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,oBAAoB,CAAC;IACnD,MAAM,GAAG,GAAG,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;IACnC,6EAA6E;IAC7E,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,GAAG,GAAG,CAAC,GAAG,GAAG,CAAC;AACrC,CAAC"}
|
package/dist/trust/types.d.ts
CHANGED
|
@@ -59,6 +59,50 @@ export interface TrustPassResult {
|
|
|
59
59
|
durationMs: number;
|
|
60
60
|
/** Optional generated artifacts (SBOM, VEX, audit log) */
|
|
61
61
|
artifacts?: Record<string, unknown>;
|
|
62
|
+
/**
|
|
63
|
+
* cognium-ai#234 (L1) — did this pass have anything to analyze?
|
|
64
|
+
*
|
|
65
|
+
* `true` (default) — the pass ran against applicable input and its `score`
|
|
66
|
+
* is a real signal (clean or not).
|
|
67
|
+
* `false` — the pass could NOT apply: no relevant files, wrong ecosystem,
|
|
68
|
+
* or a hard skip (e.g. `dependency-audit` with no `package-lock.json`,
|
|
69
|
+
* an AI-safety pass on a repo with no agent manifests). Its `score` of 100
|
|
70
|
+
* is *not* evidence of cleanliness and must be excluded from scoring so a
|
|
71
|
+
* category isn't inflated to 100 by passes that never looked.
|
|
72
|
+
*
|
|
73
|
+
* Absent ⇒ treated as `true` (byte-identical to pre-L1 scoring).
|
|
74
|
+
*/
|
|
75
|
+
applicable?: boolean;
|
|
76
|
+
}
|
|
77
|
+
/** cognium-ai#234 (L4) — reachability breakdown behind `reachability_factor`. */
|
|
78
|
+
export interface ReachabilityDetail {
|
|
79
|
+
/** The multiplier applied to the composite (0.85–1.0). */
|
|
80
|
+
factor: number;
|
|
81
|
+
/** True only when circle-ir ran AND there was ≥1 dangerous sink to assess. */
|
|
82
|
+
measured: boolean;
|
|
83
|
+
/** Total dangerous taint sinks across all analyzed files. */
|
|
84
|
+
dangerousSinks: number;
|
|
85
|
+
/** Sinks that terminate ≥1 unsanitized flow (confirmed reachable). */
|
|
86
|
+
reachableSinks: number;
|
|
87
|
+
/** Count of unsanitized source→sink flows. */
|
|
88
|
+
unsanitizedFlows: number;
|
|
89
|
+
/** reachableSinks / dangerousSinks, clamped to [0,1]. */
|
|
90
|
+
reachableRatio: number;
|
|
91
|
+
/** MAX_PENALTY in effect (for transparency / reproducibility). */
|
|
92
|
+
maxPenalty: number;
|
|
93
|
+
}
|
|
94
|
+
/** cognium-ai#234 (L1) — per-category coverage in the composite. */
|
|
95
|
+
export interface CategoryCoverage {
|
|
96
|
+
/** Category name (supply_chain | code_security | ai_safety | compliance). */
|
|
97
|
+
category: string;
|
|
98
|
+
/** True if ≥1 applicable pass contributed a real signal. */
|
|
99
|
+
validated: boolean;
|
|
100
|
+
/** Category score (0-100) when validated; null when unvalidated. */
|
|
101
|
+
score: number | null;
|
|
102
|
+
/** Passes that ran but couldn't apply (excluded), for transparency. */
|
|
103
|
+
inapplicablePasses: string[];
|
|
104
|
+
/** Default weight of this category (before re-normalization). */
|
|
105
|
+
weight: number;
|
|
62
106
|
}
|
|
63
107
|
export interface TrustScoreResult {
|
|
64
108
|
/** Weighted composite score 0-100 */
|
|
@@ -71,6 +115,17 @@ export interface TrustScoreResult {
|
|
|
71
115
|
baseTier?: TrustTier;
|
|
72
116
|
/** Critical findings that forced BLOCKED; empty when tierReason==='score'. */
|
|
73
117
|
blockingFindings?: BlockingFinding[];
|
|
118
|
+
/**
|
|
119
|
+
* Per-category coverage: which categories actually had an applicable pass
|
|
120
|
+
* vs. were `unvalidated` (no pass could look — excluded from the composite
|
|
121
|
+
* and its weight re-normalized across validated categories). Lets consumers
|
|
122
|
+
* see "supply_chain: unvalidated" instead of a fake 100, and understand
|
|
123
|
+
* which fraction of the score is real signal.
|
|
124
|
+
*/
|
|
125
|
+
categoryCoverage?: CategoryCoverage[];
|
|
126
|
+
/** Sum of the DEFAULT weights of categories that were validated (0..1).
|
|
127
|
+
* A low value means most of the score rests on few real legs. */
|
|
128
|
+
coverageWeight?: number;
|
|
74
129
|
vulnerability_score: number;
|
|
75
130
|
supply_chain_score: number;
|
|
76
131
|
exfiltration_score: number;
|
|
@@ -84,9 +139,21 @@ export interface TrustScoreResult {
|
|
|
84
139
|
poisoning_score: number;
|
|
85
140
|
provenance_score: number;
|
|
86
141
|
performance_score: number;
|
|
142
|
+
/**
|
|
143
|
+
* cognium-ai#234 (L4) — LIVE. A bounded penalty multiplier applied to the
|
|
144
|
+
* composite: `1 - MAX_PENALTY × (reachable dangerous sinks / all dangerous
|
|
145
|
+
* sinks)`, from circle-ir's taint flows. 1.0 = neutral (no dangerous sinks,
|
|
146
|
+
* nothing reachable, or circle-ir did not run). See {@link reachabilityDetail}.
|
|
147
|
+
*/
|
|
87
148
|
reachability_factor: number;
|
|
88
149
|
drift_factor: number;
|
|
89
150
|
freshness_factor: number;
|
|
151
|
+
/**
|
|
152
|
+
* cognium-ai#234 (L4) — reachability breakdown behind `reachability_factor`
|
|
153
|
+
* (additive/optional). `measured: false` means the factor is a neutral 1.0
|
|
154
|
+
* because there was nothing to assess, not because everything is safe.
|
|
155
|
+
*/
|
|
156
|
+
reachabilityDetail?: ReachabilityDetail;
|
|
90
157
|
/** Individual pass results */
|
|
91
158
|
passResults: TrustPassResult[];
|
|
92
159
|
/** ISO timestamp */
|