codebot-ai 1.7.0 → 1.9.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/dist/sarif.js ADDED
@@ -0,0 +1,176 @@
1
+ "use strict";
2
+ /**
3
+ * SARIF 2.1.0 Export for CodeBot v1.9.0
4
+ *
5
+ * Converts AuditEntry[] to SARIF 2.1.0 JSON (Static Analysis Results
6
+ * Interchange Format). Only security-relevant entries become results;
7
+ * successful executes are excluded.
8
+ *
9
+ * Rule mapping:
10
+ * security_block → CB001 / error
11
+ * policy_block → CB002 / warning
12
+ * capability_block → CB003 / warning
13
+ * error → CB004 / note
14
+ * deny → CB005 / note
15
+ *
16
+ * Usage: codebot --export-audit sarif [session-id] > results.sarif
17
+ *
18
+ * NEVER throws — export failures must not crash the agent.
19
+ */
20
+ Object.defineProperty(exports, "__esModule", { value: true });
21
+ exports.exportSarif = exportSarif;
22
+ exports.sarifToString = sarifToString;
23
+ // ── Rule Definitions ──
24
+ const RULES = [
25
+ {
26
+ id: 'CB001',
27
+ name: 'SecurityBlock',
28
+ shortDescription: { text: 'A tool call was blocked for security reasons' },
29
+ defaultConfiguration: { level: 'error' },
30
+ },
31
+ {
32
+ id: 'CB002',
33
+ name: 'PolicyBlock',
34
+ shortDescription: { text: 'A tool call was blocked by policy configuration' },
35
+ defaultConfiguration: { level: 'warning' },
36
+ },
37
+ {
38
+ id: 'CB003',
39
+ name: 'CapabilityBlock',
40
+ shortDescription: { text: 'A tool call was blocked by capability restrictions' },
41
+ defaultConfiguration: { level: 'warning' },
42
+ },
43
+ {
44
+ id: 'CB004',
45
+ name: 'ToolError',
46
+ shortDescription: { text: 'A tool call resulted in an error' },
47
+ defaultConfiguration: { level: 'note' },
48
+ },
49
+ {
50
+ id: 'CB005',
51
+ name: 'PermissionDenied',
52
+ shortDescription: { text: 'A tool call was denied by the user' },
53
+ defaultConfiguration: { level: 'note' },
54
+ },
55
+ ];
56
+ const ACTION_TO_RULE = {
57
+ security_block: { ruleId: 'CB001', level: 'error' },
58
+ policy_block: { ruleId: 'CB002', level: 'warning' },
59
+ capability_block: { ruleId: 'CB003', level: 'warning' },
60
+ error: { ruleId: 'CB004', level: 'note' },
61
+ deny: { ruleId: 'CB005', level: 'note' },
62
+ };
63
+ // ── Export Functions ──
64
+ /**
65
+ * Convert audit entries to SARIF 2.1.0 log.
66
+ * Only security-relevant entries (blocks, errors, denials) become results.
67
+ */
68
+ function exportSarif(entries, options) {
69
+ try {
70
+ const version = options?.version || '1.9.0';
71
+ const results = [];
72
+ for (const entry of entries) {
73
+ const mapping = ACTION_TO_RULE[entry.action];
74
+ if (!mapping)
75
+ continue; // Skip 'execute' — only security-relevant entries
76
+ const result = {
77
+ ruleId: mapping.ruleId,
78
+ level: mapping.level,
79
+ message: {
80
+ text: buildMessage(entry),
81
+ },
82
+ properties: {
83
+ timestamp: entry.timestamp,
84
+ sessionId: entry.sessionId,
85
+ sequence: entry.sequence,
86
+ tool: entry.tool,
87
+ action: entry.action,
88
+ },
89
+ };
90
+ // Add file location if available
91
+ const filePath = extractFilePath(entry);
92
+ if (filePath) {
93
+ result.locations = [{
94
+ physicalLocation: {
95
+ artifactLocation: { uri: filePath },
96
+ },
97
+ }];
98
+ }
99
+ results.push(result);
100
+ }
101
+ // Determine invocation times
102
+ let startTime = options?.startTime;
103
+ let endTime = options?.endTime;
104
+ if (!startTime && entries.length > 0) {
105
+ startTime = entries[0].timestamp;
106
+ }
107
+ if (!endTime && entries.length > 0) {
108
+ endTime = entries[entries.length - 1].timestamp;
109
+ }
110
+ return {
111
+ $schema: 'https://raw.githubusercontent.com/oasis-tcs/sarif-spec/main/sarif-2.1/schema/sarif-schema-2.1.0.json',
112
+ version: '2.1.0',
113
+ runs: [{
114
+ tool: {
115
+ driver: {
116
+ name: 'CodeBot',
117
+ version,
118
+ informationUri: 'https://github.com/zanderone1980/codebot-ai',
119
+ rules: RULES,
120
+ },
121
+ },
122
+ results,
123
+ invocations: [{
124
+ executionSuccessful: results.every(r => r.level !== 'error'),
125
+ startTimeUtc: startTime,
126
+ endTimeUtc: endTime,
127
+ properties: options?.sessionId ? { sessionId: options.sessionId } : undefined,
128
+ }],
129
+ }],
130
+ };
131
+ }
132
+ catch {
133
+ // Fail-safe: return minimal valid SARIF
134
+ return {
135
+ $schema: 'https://raw.githubusercontent.com/oasis-tcs/sarif-spec/main/sarif-2.1/schema/sarif-schema-2.1.0.json',
136
+ version: '2.1.0',
137
+ runs: [{
138
+ tool: {
139
+ driver: {
140
+ name: 'CodeBot',
141
+ version: options?.version || '1.9.0',
142
+ informationUri: 'https://github.com/zanderone1980/codebot-ai',
143
+ rules: RULES,
144
+ },
145
+ },
146
+ results: [],
147
+ invocations: [{ executionSuccessful: true }],
148
+ }],
149
+ };
150
+ }
151
+ }
152
+ /** Serialize SARIF log to formatted JSON string */
153
+ function sarifToString(log) {
154
+ return JSON.stringify(log, null, 2);
155
+ }
156
+ // ── Helpers ──
157
+ function buildMessage(entry) {
158
+ const parts = [];
159
+ parts.push(`Tool '${entry.tool}' — ${entry.action.replace(/_/g, ' ')}`);
160
+ if (entry.reason) {
161
+ parts.push(`: ${entry.reason}`);
162
+ }
163
+ return parts.join('');
164
+ }
165
+ function extractFilePath(entry) {
166
+ const path = entry.args?.path;
167
+ if (typeof path === 'string' && path.length > 0) {
168
+ return path;
169
+ }
170
+ const file = entry.args?.file;
171
+ if (typeof file === 'string' && file.length > 0) {
172
+ return file;
173
+ }
174
+ return undefined;
175
+ }
176
+ //# sourceMappingURL=sarif.js.map
@@ -1,8 +1,11 @@
1
1
  import { Tool } from '../types';
2
+ import { PolicyEnforcer } from '../policy';
2
3
  export declare class BatchEditTool implements Tool {
3
4
  name: string;
4
5
  description: string;
5
6
  permission: Tool['permission'];
7
+ private policyEnforcer?;
8
+ constructor(policyEnforcer?: PolicyEnforcer);
6
9
  parameters: {
7
10
  type: string;
8
11
  properties: {
@@ -42,6 +42,10 @@ class BatchEditTool {
42
42
  name = 'batch_edit';
43
43
  description = 'Apply multiple find-and-replace edits across one or more files atomically. All edits are validated before any are applied. Useful for renaming, refactoring, and coordinated multi-file changes.';
44
44
  permission = 'prompt';
45
+ policyEnforcer;
46
+ constructor(policyEnforcer) {
47
+ this.policyEnforcer = policyEnforcer;
48
+ }
45
49
  parameters = {
46
50
  type: 'object',
47
51
  properties: {
@@ -85,6 +89,14 @@ class BatchEditTool {
85
89
  errors.push(`${safety.reason}`);
86
90
  continue;
87
91
  }
92
+ // Policy: filesystem restrictions
93
+ if (this.policyEnforcer) {
94
+ const policyCheck = this.policyEnforcer.isPathWritable(filePath);
95
+ if (!policyCheck.allowed) {
96
+ errors.push(`Blocked by policy — ${policyCheck.reason}`);
97
+ continue;
98
+ }
99
+ }
88
100
  if (!byFile.has(filePath))
89
101
  byFile.set(filePath, []);
90
102
  byFile.get(filePath).push(edit);
@@ -1,8 +1,11 @@
1
1
  import { Tool } from '../types';
2
+ import { PolicyEnforcer } from '../policy';
2
3
  export declare class EditFileTool implements Tool {
3
4
  name: string;
4
5
  description: string;
5
6
  permission: Tool['permission'];
7
+ private policyEnforcer?;
8
+ constructor(policyEnforcer?: PolicyEnforcer);
6
9
  parameters: {
7
10
  type: string;
8
11
  properties: {
@@ -46,6 +46,10 @@ class EditFileTool {
46
46
  name = 'edit_file';
47
47
  description = 'Edit a file by replacing an exact string match with new content. The old_string must appear exactly once in the file. Shows a diff preview and creates an undo snapshot.';
48
48
  permission = 'prompt';
49
+ policyEnforcer;
50
+ constructor(policyEnforcer) {
51
+ this.policyEnforcer = policyEnforcer;
52
+ }
49
53
  parameters = {
50
54
  type: 'object',
51
55
  properties: {
@@ -74,6 +78,13 @@ class EditFileTool {
74
78
  if (!safety.safe) {
75
79
  return `Error: ${safety.reason}`;
76
80
  }
81
+ // Policy: filesystem restrictions (denied paths, read-only, writable scope)
82
+ if (this.policyEnforcer) {
83
+ const policyCheck = this.policyEnforcer.isPathWritable(filePath);
84
+ if (!policyCheck.allowed) {
85
+ return `Error: Blocked by policy — ${policyCheck.reason}`;
86
+ }
87
+ }
77
88
  // Security: resolve symlinks before reading
78
89
  let realPath;
79
90
  try {
@@ -1,8 +1,10 @@
1
1
  import { Tool } from '../types';
2
+ import { PolicyEnforcer } from '../policy';
2
3
  export declare class GitTool implements Tool {
3
4
  name: string;
4
5
  description: string;
5
6
  permission: Tool['permission'];
7
+ private policyEnforcer?;
6
8
  parameters: {
7
9
  type: string;
8
10
  properties: {
@@ -21,6 +23,9 @@ export declare class GitTool implements Tool {
21
23
  };
22
24
  required: string[];
23
25
  };
26
+ constructor(policyEnforcer?: PolicyEnforcer);
24
27
  execute(args: Record<string, unknown>): Promise<string>;
28
+ /** Get current git branch name. */
29
+ private getCurrentBranch;
25
30
  }
26
31
  //# sourceMappingURL=git.d.ts.map
package/dist/tools/git.js CHANGED
@@ -10,6 +10,7 @@ class GitTool {
10
10
  name = 'git';
11
11
  description = 'Run git operations. Actions: status, diff, log, commit, branch, checkout, stash, push, pull, merge, blame, tag, add, reset.';
12
12
  permission = 'prompt';
13
+ policyEnforcer;
13
14
  parameters = {
14
15
  type: 'object',
15
16
  properties: {
@@ -19,6 +20,9 @@ class GitTool {
19
20
  },
20
21
  required: ['action'],
21
22
  };
23
+ constructor(policyEnforcer) {
24
+ this.policyEnforcer = policyEnforcer;
25
+ }
22
26
  async execute(args) {
23
27
  const action = args.action;
24
28
  if (!action)
@@ -36,6 +40,20 @@ class GitTool {
36
40
  if (/clean\s+-[a-z]*f/i.test(fullCmd)) {
37
41
  return 'Error: git clean -f is blocked for safety.';
38
42
  }
43
+ // Policy: block push to main/master when never_push_main=true
44
+ if (action === 'push' && this.policyEnforcer?.isMainPushBlocked()) {
45
+ const currentBranch = this.getCurrentBranch(cwd);
46
+ if (currentBranch === 'main' || currentBranch === 'master') {
47
+ return 'Error: Pushing to main/master is blocked by policy (git.never_push_main=true). Create a feature branch first.';
48
+ }
49
+ }
50
+ // Policy: block commit on main/master when always_branch=true
51
+ if (action === 'commit' && this.policyEnforcer?.shouldAlwaysBranch()) {
52
+ const currentBranch = this.getCurrentBranch(cwd);
53
+ if (currentBranch === 'main' || currentBranch === 'master') {
54
+ return 'Error: Committing to main/master is blocked by policy (git.always_branch=true). Create a feature branch first.';
55
+ }
56
+ }
39
57
  try {
40
58
  const output = (0, child_process_1.execSync)(fullCmd, {
41
59
  cwd,
@@ -53,6 +71,19 @@ class GitTool {
53
71
  return `Exit ${e.status || 1}${stdout ? `\n${stdout}` : ''}${stderr ? `\nError: ${stderr}` : ''}`;
54
72
  }
55
73
  }
74
+ /** Get current git branch name. */
75
+ getCurrentBranch(cwd) {
76
+ try {
77
+ return (0, child_process_1.execSync)('git rev-parse --abbrev-ref HEAD', {
78
+ cwd,
79
+ encoding: 'utf-8',
80
+ timeout: 5000,
81
+ }).trim();
82
+ }
83
+ catch {
84
+ return '';
85
+ }
86
+ }
56
87
  }
57
88
  exports.GitTool = GitTool;
58
89
  //# sourceMappingURL=git.js.map
@@ -1,8 +1,9 @@
1
1
  import { Tool, ToolSchema } from '../types';
2
+ import { PolicyEnforcer } from '../policy';
2
3
  export { EditFileTool } from './edit';
3
4
  export declare class ToolRegistry {
4
5
  private tools;
5
- constructor(projectRoot?: string);
6
+ constructor(projectRoot?: string, policyEnforcer?: PolicyEnforcer);
6
7
  register(tool: Tool): void;
7
8
  get(name: string): Tool | undefined;
8
9
  getSchemas(): ToolSchema[];
@@ -34,12 +34,12 @@ var edit_2 = require("./edit");
34
34
  Object.defineProperty(exports, "EditFileTool", { enumerable: true, get: function () { return edit_2.EditFileTool; } });
35
35
  class ToolRegistry {
36
36
  tools = new Map();
37
- constructor(projectRoot) {
38
- // Core file tools
37
+ constructor(projectRoot, policyEnforcer) {
38
+ // Core file tools — policy-enforced tools receive the enforcer
39
39
  this.register(new read_1.ReadFileTool());
40
- this.register(new write_1.WriteFileTool());
41
- this.register(new edit_1.EditFileTool());
42
- this.register(new batch_edit_1.BatchEditTool());
40
+ this.register(new write_1.WriteFileTool(policyEnforcer));
41
+ this.register(new edit_1.EditFileTool(policyEnforcer));
42
+ this.register(new batch_edit_1.BatchEditTool(policyEnforcer));
43
43
  this.register(new execute_1.ExecuteTool());
44
44
  this.register(new glob_1.GlobTool());
45
45
  this.register(new grep_1.GrepTool());
@@ -51,7 +51,7 @@ class ToolRegistry {
51
51
  this.register(new browser_1.BrowserTool());
52
52
  this.register(new routine_1.RoutineTool());
53
53
  // v1.4.0 — intelligence & dev tools
54
- this.register(new git_1.GitTool());
54
+ this.register(new git_1.GitTool(policyEnforcer));
55
55
  this.register(new code_analysis_1.CodeAnalysisTool());
56
56
  this.register(new multi_search_1.MultiSearchTool());
57
57
  this.register(new task_planner_1.TaskPlannerTool());
@@ -1,8 +1,11 @@
1
1
  import { Tool } from '../types';
2
+ import { PolicyEnforcer } from '../policy';
2
3
  export declare class WriteFileTool implements Tool {
3
4
  name: string;
4
5
  description: string;
5
6
  permission: Tool['permission'];
7
+ private policyEnforcer?;
8
+ constructor(policyEnforcer?: PolicyEnforcer);
6
9
  parameters: {
7
10
  type: string;
8
11
  properties: {
@@ -44,6 +44,10 @@ class WriteFileTool {
44
44
  name = 'write_file';
45
45
  description = 'Create a new file or overwrite an existing file with the given content. Automatically saves an undo snapshot for existing files.';
46
46
  permission = 'prompt';
47
+ policyEnforcer;
48
+ constructor(policyEnforcer) {
49
+ this.policyEnforcer = policyEnforcer;
50
+ }
47
51
  parameters = {
48
52
  type: 'object',
49
53
  properties: {
@@ -68,6 +72,13 @@ class WriteFileTool {
68
72
  if (!safety.safe) {
69
73
  return `Error: ${safety.reason}`;
70
74
  }
75
+ // Policy: filesystem restrictions (denied paths, read-only, writable scope)
76
+ if (this.policyEnforcer) {
77
+ const policyCheck = this.policyEnforcer.isPathWritable(filePath);
78
+ if (!policyCheck.allowed) {
79
+ return `Error: Blocked by policy — ${policyCheck.reason}`;
80
+ }
81
+ }
71
82
  // Security: secret detection (warn but don't block)
72
83
  const secrets = (0, secrets_1.scanForSecrets)(content);
73
84
  let warning = '';
package/dist/types.d.ts CHANGED
@@ -33,6 +33,7 @@ export interface ProviderConfig {
33
33
  }
34
34
  export interface LLMProvider {
35
35
  name: string;
36
+ temperature?: number;
36
37
  chat(messages: Message[], tools?: ToolSchema[]): AsyncGenerator<StreamEvent>;
37
38
  listModels?(): Promise<string[]>;
38
39
  }
@@ -70,6 +71,10 @@ export interface AgentEvent {
70
71
  };
71
72
  error?: string;
72
73
  usage?: UsageStats;
74
+ risk?: {
75
+ score: number;
76
+ level: string;
77
+ };
73
78
  }
74
79
  export interface Config {
75
80
  provider: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codebot-ai",
3
- "version": "1.7.0",
3
+ "version": "1.9.0",
4
4
  "description": "Zero-dependency autonomous AI agent. Code, browse, search, automate. Works with any LLM — Ollama, Claude, GPT, Gemini, DeepSeek, Groq, Mistral, Grok.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",