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:
Ajax Davis 2025-12-31 22:55:56 +10:00
parent 2d9b06020d
commit 5d2096fb5d
499 changed files with 50782 additions and 238 deletions

View 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

View 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"
}
}

View 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;

View 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"]
}

View 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,
});