@feng0531/dsh-code-review 0.37.0 → 0.39.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 +39 -406
- package/CORE-ALIGNMENT-TASKS.md +47 -0
- package/CORE-SOURCE-INVENTORY.md +168 -0
- package/DEVELOPMENT.md +52 -0
- package/README.md +76 -145
- package/RELEASE-NOTES.md +26 -0
- package/index.mjs +1 -1
- package/package.json +2 -2
- package/src/defect-guidance.mjs +5 -4
- package/src/dependency-chain.mjs +33 -0
- package/src/protocol-guidance.mjs +24 -0
- package/src/snapshot.mjs +6 -2
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@feng0531/dsh-code-review",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.39.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE",
|
|
6
6
|
"publishConfig": { "access": "public", "registry": "https://registry.npmjs.org/" },
|
|
7
7
|
"main": "./index.mjs",
|
|
8
8
|
"exports": { ".": "./index.mjs", "./package.json": "./package.json", "./locale/*.json": "./locale/*.json" },
|
|
9
|
-
"files": ["index.mjs", "cordis.patch.yml", "src/", "locale/", "README.md", "CHANGELOG.md", "LICENSE", "LICENSES/"],
|
|
9
|
+
"files": ["index.mjs", "cordis.patch.yml", "src/", "locale/", "README.md", "CHANGELOG.md", "RELEASE-NOTES.md", "DEVELOPMENT.md", "CORE-ALIGNMENT-TASKS.md", "CORE-SOURCE-INVENTORY.md", "LICENSE", "LICENSES/"],
|
|
10
10
|
"dsh": { "bundle": { "patch": "./cordis.patch.yml" } },
|
|
11
11
|
"repository": { "type": "git", "url": "https://github.com/f-e-n-g-0531/dsh-code-review.git" },
|
|
12
12
|
"type": "module",
|
package/src/defect-guidance.mjs
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import {protocolGuidance} from './protocol-guidance.mjs';
|
|
1
2
|
// Original defect checklists, not executable rules or proof of a defect.
|
|
2
3
|
const common = ['Only report a concrete defect with trigger, impact and changed-code evidence.', 'Check before/after behavior and counterexamples; absent context means uncertainty, not an invented contract.'];
|
|
3
4
|
const checklists = {
|
|
@@ -17,14 +18,14 @@ const checklists = {
|
|
|
17
18
|
configuration: ['Check changed configuration keys against consumer/schema evidence, type/unit/default mismatches and compatibility.', 'Check exposed secrets, privilege expansion and unsafe interpolation only with concrete supported evidence.']
|
|
18
19
|
};
|
|
19
20
|
export function defectGuidance(filePath, sourceText = '') {
|
|
20
|
-
const lower = filePath.toLowerCase();
|
|
21
|
+
const lower = filePath.toLowerCase(), protocol = protocolGuidance(filePath);
|
|
21
22
|
const first = typeof sourceText === 'string' ? sourceText.slice(0,8192).split('\n').map(s=>s.trim()).find(Boolean) ?? '' : '';
|
|
22
23
|
const sniffed = lower.endsWith('.m') ? (/^(?:#(?:import|include|pragma|if|define)\b|@(?:import|interface|implementation|class|protocol)\b|\/\/|\/\*)/.test(first) ? 'objc' : 'matlab') : null;
|
|
23
24
|
const specialized = /\.(shader|hlsl|glsl|cginc|hlsli|compute)$/.test(lower) ? 'shader' : sniffed ?? (/\.mm$/.test(lower) ? 'objc' : /\.swift$/.test(lower) ? 'swift' : null) ?? (/\.java$/.test(lower) ? 'java' : /\.go$/.test(lower) ? 'go' : /\.(py|pyi)$/.test(lower) ? 'python' : /\.rs$/.test(lower) ? 'rust' : /\.(kt|kts)$/.test(lower) ? 'kotlin' : /\.(php|phtml)$/.test(lower) ? 'php' : null);
|
|
24
|
-
const language = specialized ?? (/\.(c|cc|cpp|cxx|h|hh|hpp|hxx)$/.test(lower) ? 'cpp' : lower.endsWith('.cs') ? 'csharp' : /\.(js|jsx|mjs|cjs|ts|tsx)$/.test(lower) ? 'javascript' : /\.(json|yaml|yml|toml|ini|xml|properties)$/.test(lower) ? 'configuration' : 'generic');
|
|
25
|
+
const language = protocol?.language ?? specialized ?? (/\.(c|cc|cpp|cxx|h|hh|hpp|hxx)$/.test(lower) ? 'cpp' : lower.endsWith('.cs') ? 'csharp' : /\.(js|jsx|mjs|cjs|ts|tsx)$/.test(lower) ? 'javascript' : /\.(json|yaml|yml|toml|ini|xml|properties)$/.test(lower) ? 'configuration' : 'generic');
|
|
25
26
|
const fileType = /^\.github\/workflows\/.+\.(yaml|yml)$/.test(lower) ? 'github-workflow' : /(?:^|\/)(package\.json|cargo\.toml|pom\.xml|build\.gradle|composer\.json)$/.test(lower) ? 'dependency-manifest' : undefined;
|
|
26
27
|
const purpose = fileType ?? (/\.(ftl|ftlh|ftlx|hbs|mustache|jinja2|j2|pug|astro)$/.test(lower) ? 'template' : /\.(tf|hcl|tfvars|bicep|nix)$/.test(lower) ? 'infrastructure' : /\.(proto|thrift|capnp|graphql|gql|prisma)$/.test(lower) ? 'protocol-schema' : undefined);
|
|
27
|
-
const purposeChecks = purpose === 'template' ? ['Check escaping for the actual HTML/attribute/URL/script context, untrusted raw output and template execution boundaries.', 'Check missing/null variables and changed conditional branches against captured callers; do not invent engine defaults.'] : purpose === 'infrastructure' ? ['Check privilege/network exposure, secret/state handling and untrusted interpolation against actual provider semantics.', 'Check replacement/deletion, dependency order and changed defaults for concrete data loss or service disruption; configuration is not an applied plan.'] : purpose === 'protocol-schema' ?
|
|
28
|
+
const purposeChecks = purpose === 'template' ? ['Check escaping for the actual HTML/attribute/URL/script context, untrusted raw output and template execution boundaries.', 'Check missing/null variables and changed conditional branches against captured callers; do not invent engine defaults.'] : purpose === 'infrastructure' ? ['Check privilege/network exposure, secret/state handling and untrusted interpolation against actual provider semantics.', 'Check replacement/deletion, dependency order and changed defaults for concrete data loss or service disruption; configuration is not an applied plan.'] : purpose === 'protocol-schema' ? protocol.checks : [];
|
|
28
29
|
const typeChecks = fileType === 'github-workflow' ? ['Check event trust boundaries, token permissions and fork/PR-controlled inputs before privileged commands or secret exposure.', 'Check expression interpolation into shell commands, checkout/ref provenance, artifact/cache trust and required job dependency gates.'] : fileType === 'dependency-manifest' ? ['Check changed dependency versions/features against consumers, lock/resolution evidence and runtime compatibility; do not invent a vulnerability from a package name.', 'Check executable install/build hooks, publish/export entry points and platform constraints for concrete changed behavior.'] : [];
|
|
29
|
-
return { version:
|
|
30
|
+
return { version:5, language, ...(sniffed ? {selection:'bounded-first-nonblank-line-hint'} : {}), ...(purpose ? {fileType:purpose} : {}), checks:[...common,...(checklists[language] ?? []),...typeChecks,...purposeChecks], notice:'Guidance is not evidence, a semantic parser, a style rule or permission to read outside the snapshot.' };
|
|
30
31
|
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import {contextCandidates} from './context-candidates.mjs';
|
|
2
|
+
import {gitContextIndex} from './tracked-context.mjs';
|
|
3
|
+
import {readLocal} from './content.mjs';
|
|
4
|
+
// Fixed-depth literal navigation, not symbol binding. Never enumerate the repository.
|
|
5
|
+
export async function captureDependencyChain(root,files,options={}){
|
|
6
|
+
const signal=AbortSignal.any([AbortSignal.timeout(30000),...(options.signal?[options.signal]:[])]);
|
|
7
|
+
const scoped={...options,maxFileBytes:Math.min(options.maxFileBytes??256*1024,256*1024),signal},probes=new Set(),visited=new Set(files.map(f=>f.path)),context=[],candidates=[];
|
|
8
|
+
const reserved=new Set([...(options.contextPaths??[]),...(options.rulePaths??[])]),limit=Math.min(options.maxContextFiles??20,20);
|
|
9
|
+
let frontier=files,index,bytes=0,truncated=false,depth=0;
|
|
10
|
+
const onProbe=paths=>{for(const p of paths)probes.add(p);if(probes.size>512)throw new Error('Git context lookup limit exceeded');};
|
|
11
|
+
for(depth=1;depth<=3&&frontier.length;depth++){
|
|
12
|
+
signal.throwIfAborted();contextCandidates(frontier,[],{onProbe});
|
|
13
|
+
index=await gitContextIndex(root,{...scoped,probePaths:[...probes]});
|
|
14
|
+
const plan=contextCandidates(frontier,index.paths);truncated ||= plan.truncated;
|
|
15
|
+
const next=[];
|
|
16
|
+
for(const entry of plan.candidates){
|
|
17
|
+
if(visited.has(entry.path)||reserved.has(entry.path))continue;
|
|
18
|
+
visited.add(entry.path);const candidate={...entry,depth};candidates.push(candidate);
|
|
19
|
+
if(candidates.length>20)throw new Error('Dependency candidate limit exceeded');
|
|
20
|
+
if(context.length+new Set(options.contextPaths??[]).size>=limit){candidate.status='blocked';candidate.reason='Context file limit exceeded';continue;}
|
|
21
|
+
try{
|
|
22
|
+
const item={path:entry.path,...await readLocal(root,entry.path,scoped)};
|
|
23
|
+
bytes+=item.bytes;if(bytes>1024*1024)throw new Error('Dependency source byte limit exceeded');
|
|
24
|
+
context.push(item);candidate.status='captured';
|
|
25
|
+
next.push({path:item.path,eligibility:'reviewable',right:item});
|
|
26
|
+
}catch(error){signal.throwIfAborted();if(error.message==='Dependency source byte limit exceeded')throw error;candidate.status='blocked';candidate.reason=error.message;}
|
|
27
|
+
}
|
|
28
|
+
frontier=next;
|
|
29
|
+
}
|
|
30
|
+
if(frontier.length)truncated=true;
|
|
31
|
+
const indexOptions={...scoped,probePaths:[...probes]};
|
|
32
|
+
return {context,index,indexOptions,autoContext:{candidates,capturedPaths:context.map(c=>c.path),truncated,maxDepth:3,probeCount:probes.size,sourceBytes:bytes,notice:'Bounded current-side literal dependency navigation, depth <=3, 512 shared exact probes, 20 candidates/context slots, 1MiB source bytes and 30s through verification. Not symbol binding, caller coverage or complete definitions.'}};
|
|
33
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// Original, bounded review hints; no parser, I/O, schema enforcement or defect proof.
|
|
2
|
+
const checks={
|
|
3
|
+
protobuf:[
|
|
4
|
+
'Check numeric tag/enum value reuse and removed field reservations against persisted messages and actual consumers; additive fields alone are not defects.',
|
|
5
|
+
'Check oneof, presence, defaults and type changes for the deployed syntax/edition/runtime; distinguish binary wire compatibility from JSON names and generated API compatibility.'
|
|
6
|
+
],
|
|
7
|
+
thrift:[
|
|
8
|
+
'Check field IDs/types, requiredness and defaults against the actual protocol and old/new consumers; declaration order is not identity and Thrift has no reserved keyword.',
|
|
9
|
+
'Check method names, oneway/reply semantics, exceptions and include/namespace bindings against generated clients; additive optional fields alone do not prove breakage.'
|
|
10
|
+
],
|
|
11
|
+
capnp:[
|
|
12
|
+
'Check ordinals, explicit type IDs, fixed-width storage and default XOR encoding against existing data and deployed readers; do not infer identity from declaration order.',
|
|
13
|
+
'Check union/layout evolution and generated API consumers separately from wire compatibility; same-ordinal rename alone usually does not break wire data.'
|
|
14
|
+
],
|
|
15
|
+
graphql:[
|
|
16
|
+
'Check input non-null/default changes, removed fields/arguments and operation compatibility using actual clients; GraphQL has no numeric field tags.',
|
|
17
|
+
'Check output null bubbling, enum consumers and resolver authorization using captured implementation; absent auth directives or additive fields/enum values alone do not prove a defect.'
|
|
18
|
+
],
|
|
19
|
+
prisma:[
|
|
20
|
+
'Check provider, relationMode, relation actions, constraints and nullability against the deployed database, migration and generated-client consumers; schema is not an applied migration.',
|
|
21
|
+
'Check concrete data loss, migration order and client rollout counterexamples; do not require indexes or tenant fields from names alone or invent database enforcement in emulated relations.'
|
|
22
|
+
]
|
|
23
|
+
};
|
|
24
|
+
export function protocolGuidance(filePath){const lower=filePath.toLowerCase();const language=/\.proto$/.test(lower)?'protobuf':/\.thrift$/.test(lower)?'thrift':/\.capnp$/.test(lower)?'capnp':/\.(graphql|gql)$/.test(lower)?'graphql':/\.prisma$/.test(lower)?'prisma':undefined;return language?{language,checks:[...checks[language]]}:undefined;}
|
package/src/snapshot.mjs
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import {captureDependencyChain} from './dependency-chain.mjs';
|
|
1
2
|
import {captureSvnCallers} from './svn-caller-capture.mjs';
|
|
2
3
|
import { captureCallers } from './caller-capture.mjs';
|
|
3
4
|
import { validateCallerScopes } from './caller-index.mjs';
|
|
@@ -49,7 +50,10 @@ export async function captureSnapshot(cwd, options = {}) {
|
|
|
49
50
|
const rules = await captureProjectRules(snapshot.root, rulePaths, { signal: options.signal, files: snapshot.files });
|
|
50
51
|
const context = [];
|
|
51
52
|
let autoContext, index, indexOptions = options;
|
|
52
|
-
if (options.autoContext === true) {
|
|
53
|
+
if (options.autoContext === true && type==='git') {
|
|
54
|
+
const chain=await captureDependencyChain(snapshot.root,snapshot.files,options);
|
|
55
|
+
context.push(...chain.context);autoContext=chain.autoContext;index=chain.index;indexOptions=chain.indexOptions;
|
|
56
|
+
} else if (options.autoContext === true) {
|
|
53
57
|
if(type==='git'){const probes=new Set();contextCandidates(snapshot.files,[],{onProbe:paths=>{for(const p of paths)probes.add(p);if(probes.size>512)throw new Error('Git context lookup limit exceeded');}});indexOptions={...options,probePaths:[...probes]};}
|
|
54
58
|
index = await (type === 'git' ? gitContextIndex : svnContextIndex)(snapshot.root, indexOptions);
|
|
55
59
|
const plan = contextCandidates(snapshot.files, index.paths);
|
|
@@ -90,7 +94,7 @@ export async function captureSnapshot(cwd, options = {}) {
|
|
|
90
94
|
}
|
|
91
95
|
// Re-capture verifies both selected sides while collecting explicit context.
|
|
92
96
|
if ((await capture(cwd, options)).id !== snapshot.id) throw new Error('Snapshot changed while collecting context');
|
|
93
|
-
for (const item of context) if ((await readLocal(snapshot.root, item.path,
|
|
97
|
+
for (const item of context) if ((await readLocal(snapshot.root, item.path, indexOptions)).hash !== item.hash) throw new Error('Context changed during capture');
|
|
94
98
|
if (index && (await (type === 'git' ? gitContextIndex : svnContextIndex)(snapshot.root, indexOptions)).fingerprint !== index.fingerprint) throw new Error('Tracked context index changed during capture');
|
|
95
99
|
const verifiedRules = await captureProjectRules(snapshot.root, rulePaths, { signal: options.signal, files: snapshot.files });
|
|
96
100
|
if (JSON.stringify(verifiedRules) !== JSON.stringify(rules)) throw new Error('Rules changed during capture');
|