feat: add 100+ official TPMJS tools
Implements a comprehensive suite of AI SDK v6 tools across multiple categories: - Research (5): page-brief, compare-pages, source-credibility, claim-checklist, timeline-from-text - Web (10): fetch-text, links-catalog, extract-meta, extract-json-ld, redirect-trace, sitemap-read, rss-read, table-extract, robots-policy, url-normalize - Data (15): csv-parse, csv-stringify, json-repair, json-schema-validate, yaml-parse, yaml-stringify, text-chunk, normalize-whitespace, dedupe-by-key, pivot, rows-filter, rows-sort, rows-group-aggregate, rows-join, schema-infer - Doc (12): toc-generate, glossary-build, faq-from-text, executive-brief, decision-record-adr, prd-outline, acceptance-criteria, style-rewrite - Eng (12): diff-text-unified, env-var-docs-generate, dependency-audit-lite, conventional-commit-suggest, markdown-lint-basic, test-case-generate, stacktrace-parse, release-notes, changelog-entry, release-checklist - Security (7): redact-secrets, secret-scan-text, url-risk-heuristic, csp-compose, hardening-checklist-web, access-control-matrix, data-classification-heuristic - Stats (9): effect-size-suite, bootstrap-ci, permutation-test, multiple-testing-adjust, linear-regression-ols, logistic-regression, time-series-decompose-lite, anomaly-detect-mad - Ops (7): slo-draft, runbook-draft, postmortem-draft, postmortem-action-extractor, error-log-triage, coverage-tracker, monitoring-gap-analysis - Agent (15): prompt-to-workflow-skeleton, workflow-validate-io, workflow-explain, workflow-cost-estimate, tool-call-accuracy-score, eval-fixture-build, guardrail-policy-draft, workflow-auto-repair, tool-selection-plan, novelty-score-workflow, workflow-variant-generate, config-normalize, recipe-* - Utility (8): base64-encode, base64-decode, hash-text, regex-extract, template-render, date-parse, json-path-query, url-parse - HTML (3): html-sanitize, html-to-markdown, markdown-to-html All tools follow AI SDK v6 pattern with tool() and jsonSchema<T>(). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
2d9b06020d
commit
5d2096fb5d
499 changed files with 50782 additions and 238 deletions
11
packages/tools/official/csp-compose/CHANGELOG.md
Normal file
11
packages/tools/official/csp-compose/CHANGELOG.md
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
# @tpmjs/tools-csp-compose
|
||||
|
||||
## 0.1.0
|
||||
|
||||
### Initial Release
|
||||
|
||||
- Initial implementation of CSP Compose tool
|
||||
- Validates CSP directive names and source values
|
||||
- Checks for strict CSP patterns (nonces, hashes, no unsafe-inline/eval)
|
||||
- Generates security warnings for common issues
|
||||
- Formats CSP header string from directive configurations
|
||||
60
packages/tools/official/csp-compose/package.json
Normal file
60
packages/tools/official/csp-compose/package.json
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
{
|
||||
"name": "@tpmjs/tools-csp-compose",
|
||||
"version": "0.1.0",
|
||||
"description": "Compose Content Security Policy headers from directive configurations",
|
||||
"type": "module",
|
||||
"keywords": ["tpmjs", "security", "csp", "content-security-policy"],
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/index.d.ts",
|
||||
"default": "./dist/index.js"
|
||||
}
|
||||
},
|
||||
"files": ["dist"],
|
||||
"scripts": {
|
||||
"build": "tsup",
|
||||
"dev": "tsup --watch",
|
||||
"type-check": "tsc --noEmit",
|
||||
"clean": "rm -rf dist .turbo"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tpmjs/tsconfig": "workspace:*",
|
||||
"tsup": "^8.3.5",
|
||||
"typescript": "^5.9.3"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/anthropics/tpmjs.git",
|
||||
"directory": "packages/tools/official/csp-compose"
|
||||
},
|
||||
"homepage": "https://tpmjs.com",
|
||||
"license": "MIT",
|
||||
"tpmjs": {
|
||||
"category": "security",
|
||||
"frameworks": ["vercel-ai"],
|
||||
"tools": [
|
||||
{
|
||||
"name": "cspComposeTool",
|
||||
"description": "Compose a Content Security Policy header from directive configurations",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "policies",
|
||||
"type": "object",
|
||||
"description": "CSP directives mapped to source arrays",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"returns": {
|
||||
"type": "CSPResult",
|
||||
"description": "Object with header string, directives array, and isStrict boolean"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"ai": "6.0.0-beta.124"
|
||||
}
|
||||
}
|
||||
258
packages/tools/official/csp-compose/src/index.ts
Normal file
258
packages/tools/official/csp-compose/src/index.ts
Normal file
|
|
@ -0,0 +1,258 @@
|
|||
/**
|
||||
* CSP Compose Tool for TPMJS
|
||||
* Composes Content Security Policy headers from directive configurations.
|
||||
* Validates directives and checks for strict CSP patterns.
|
||||
*/
|
||||
|
||||
import { jsonSchema, tool } from 'ai';
|
||||
|
||||
/**
|
||||
* Output interface for CSP composition
|
||||
*/
|
||||
export interface CSPResult {
|
||||
header: string;
|
||||
directives: Array<{
|
||||
directive: string;
|
||||
sources: string[];
|
||||
}>;
|
||||
isStrict: boolean;
|
||||
warnings: string[];
|
||||
}
|
||||
|
||||
type CSPComposeInput = {
|
||||
policies: Record<string, string[]>;
|
||||
};
|
||||
|
||||
/**
|
||||
* Valid CSP directive names
|
||||
*/
|
||||
const VALID_DIRECTIVES = new Set([
|
||||
'default-src',
|
||||
'script-src',
|
||||
'style-src',
|
||||
'img-src',
|
||||
'font-src',
|
||||
'connect-src',
|
||||
'media-src',
|
||||
'object-src',
|
||||
'frame-src',
|
||||
'child-src',
|
||||
'worker-src',
|
||||
'manifest-src',
|
||||
'base-uri',
|
||||
'form-action',
|
||||
'frame-ancestors',
|
||||
'report-uri',
|
||||
'report-to',
|
||||
'upgrade-insecure-requests',
|
||||
'block-all-mixed-content',
|
||||
]);
|
||||
|
||||
/**
|
||||
* Unsafe CSP sources that weaken security
|
||||
*/
|
||||
const UNSAFE_SOURCES = new Set(["'unsafe-inline'", "'unsafe-eval'", "'unsafe-hashes'"]);
|
||||
|
||||
/**
|
||||
* Validates a CSP directive name
|
||||
*/
|
||||
function isValidDirective(directive: string): boolean {
|
||||
return VALID_DIRECTIVES.has(directive);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a policy is considered strict
|
||||
* A strict CSP:
|
||||
* - Uses nonces or hashes for scripts
|
||||
* - Avoids 'unsafe-inline' and 'unsafe-eval'
|
||||
* - Has a restrictive default-src
|
||||
*/
|
||||
function isStrictCSP(policies: Record<string, string[]>): boolean {
|
||||
// Check for unsafe sources in critical directives
|
||||
const criticalDirectives = ['default-src', 'script-src', 'style-src'];
|
||||
|
||||
for (const directive of criticalDirectives) {
|
||||
const sources = policies[directive] || [];
|
||||
for (const source of sources) {
|
||||
if (UNSAFE_SOURCES.has(source)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check if script-src uses nonces or hashes
|
||||
const scriptSrc = policies['script-src'] || policies['default-src'] || [];
|
||||
const hasNonceOrHash = scriptSrc.some(
|
||||
(source) => source.startsWith("'nonce-") || source.startsWith("'sha")
|
||||
);
|
||||
|
||||
// Check for restrictive default-src
|
||||
const defaultSrc = policies['default-src'] || [];
|
||||
const hasRestrictiveDefault = defaultSrc.includes("'self'") || defaultSrc.includes("'none'");
|
||||
|
||||
return hasNonceOrHash && hasRestrictiveDefault;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates warnings for common CSP issues
|
||||
*/
|
||||
function generateWarnings(policies: Record<string, string[]>): string[] {
|
||||
const warnings: string[] = [];
|
||||
|
||||
// Check for unsafe-inline
|
||||
for (const [directive, sources] of Object.entries(policies)) {
|
||||
if (sources.includes("'unsafe-inline'")) {
|
||||
warnings.push(
|
||||
`${directive} contains 'unsafe-inline' which allows inline scripts/styles and weakens CSP protection`
|
||||
);
|
||||
}
|
||||
if (sources.includes("'unsafe-eval'")) {
|
||||
warnings.push(
|
||||
`${directive} contains 'unsafe-eval' which allows eval() and similar functions, creating XSS risks`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Check for wildcard sources
|
||||
for (const [directive, sources] of Object.entries(policies)) {
|
||||
if (sources.includes('*')) {
|
||||
warnings.push(`${directive} contains wildcard (*) which allows resources from any origin`);
|
||||
}
|
||||
if (sources.some((s) => s.startsWith('*.'))) {
|
||||
warnings.push(
|
||||
`${directive} contains subdomain wildcard (*.domain) which may be overly permissive`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Check if default-src is missing
|
||||
if (!policies['default-src']) {
|
||||
warnings.push(
|
||||
"Missing 'default-src' directive - consider adding a restrictive default fallback"
|
||||
);
|
||||
}
|
||||
|
||||
// Check for missing object-src
|
||||
if (!policies['object-src']) {
|
||||
warnings.push(
|
||||
"Missing 'object-src' directive - consider adding \"object-src 'none'\" to block plugins"
|
||||
);
|
||||
}
|
||||
|
||||
// Check for missing base-uri
|
||||
if (!policies['base-uri']) {
|
||||
warnings.push(
|
||||
"Missing 'base-uri' directive - consider adding \"base-uri 'self'\" to prevent base tag injection"
|
||||
);
|
||||
}
|
||||
|
||||
return warnings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats sources for a directive
|
||||
*/
|
||||
function formatSources(sources: string[]): string {
|
||||
// Remove duplicates and sort
|
||||
const uniqueSources = Array.from(new Set(sources));
|
||||
return uniqueSources.join(' ');
|
||||
}
|
||||
|
||||
/**
|
||||
* CSP Compose Tool
|
||||
* Composes a Content Security Policy header from directive configurations
|
||||
*/
|
||||
export const cspComposeTool = tool({
|
||||
description:
|
||||
'Compose a Content Security Policy (CSP) header from directive configurations. Validates directives, checks for security issues, and determines if the policy is strict. Returns the formatted CSP header string, directive details, and security warnings.',
|
||||
inputSchema: jsonSchema<CSPComposeInput>({
|
||||
type: 'object',
|
||||
properties: {
|
||||
policies: {
|
||||
type: 'object',
|
||||
description:
|
||||
'CSP directives mapped to arrays of source values. Example: { "default-src": ["\'self\'"], "script-src": ["\'nonce-abc123\'", "https://cdn.example.com"] }',
|
||||
additionalProperties: {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
required: ['policies'],
|
||||
additionalProperties: false,
|
||||
}),
|
||||
async execute({ policies }): Promise<CSPResult> {
|
||||
// Validate input
|
||||
if (!policies || typeof policies !== 'object') {
|
||||
throw new Error('Policies must be an object mapping directives to source arrays');
|
||||
}
|
||||
|
||||
if (Object.keys(policies).length === 0) {
|
||||
throw new Error('At least one CSP directive is required');
|
||||
}
|
||||
|
||||
// Validate and build directives
|
||||
const directives: Array<{ directive: string; sources: string[] }> = [];
|
||||
const headerParts: string[] = [];
|
||||
|
||||
for (const [directive, sources] of Object.entries(policies)) {
|
||||
// Validate directive name
|
||||
if (!isValidDirective(directive)) {
|
||||
throw new Error(
|
||||
`Invalid CSP directive: "${directive}". Must be one of: ${Array.from(VALID_DIRECTIVES).join(', ')}`
|
||||
);
|
||||
}
|
||||
|
||||
// Validate sources is an array
|
||||
if (!Array.isArray(sources)) {
|
||||
throw new Error(`Sources for directive "${directive}" must be an array`);
|
||||
}
|
||||
|
||||
// Handle directives without values (flags)
|
||||
if (directive === 'upgrade-insecure-requests' || directive === 'block-all-mixed-content') {
|
||||
directives.push({ directive, sources: [] });
|
||||
headerParts.push(directive);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Validate sources is not empty for value directives
|
||||
if (sources.length === 0) {
|
||||
throw new Error(`Directive "${directive}" requires at least one source value`);
|
||||
}
|
||||
|
||||
// Validate each source
|
||||
for (const source of sources) {
|
||||
if (typeof source !== 'string' || source.trim().length === 0) {
|
||||
throw new Error(
|
||||
`Invalid source value in "${directive}": sources must be non-empty strings`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Build directive string
|
||||
const formattedSources = formatSources(sources);
|
||||
directives.push({ directive, sources: [...sources] });
|
||||
headerParts.push(`${directive} ${formattedSources}`);
|
||||
}
|
||||
|
||||
// Build the final header
|
||||
const header = headerParts.join('; ');
|
||||
|
||||
// Check if the policy is strict
|
||||
const isStrict = isStrictCSP(policies);
|
||||
|
||||
// Generate warnings
|
||||
const warnings = generateWarnings(policies);
|
||||
|
||||
return {
|
||||
header,
|
||||
directives,
|
||||
isStrict,
|
||||
warnings,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
export default cspComposeTool;
|
||||
11
packages/tools/official/csp-compose/tsconfig.json
Normal file
11
packages/tools/official/csp-compose/tsconfig.json
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"extends": "@tpmjs/tsconfig/base.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "dist",
|
||||
"rootDir": "src",
|
||||
"incremental": false,
|
||||
"composite": false
|
||||
},
|
||||
"include": ["src"],
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
10
packages/tools/official/csp-compose/tsup.config.ts
Normal file
10
packages/tools/official/csp-compose/tsup.config.ts
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
import { defineConfig } from 'tsup';
|
||||
|
||||
export default defineConfig({
|
||||
entry: ['src/index.ts'],
|
||||
format: ['esm'],
|
||||
dts: true,
|
||||
clean: true,
|
||||
treeshake: true,
|
||||
splitting: false,
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue