GraphQL Schema — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited GraphQL Schema (Agent Skill) and scored it 96/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 1 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 1 flagged
A bulleted imperative like {match} tells the agent to never reveal, disclose, or mention something to the user. Used adversarially it can instruct the agent to hide its tool calls or lie about what it did — stripping the transparency a user relies on to trust the agent.
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
A GraphQL schema is a public contract that clients build against for years, and a resolver layer is an open invitation for N+1 queries and abusive nesting. This skill designs schemas clients love and servers survive: model the domain rather than the database, batch every relationship resolver, and put hard limits on what a single query may cost - because removing a shipped field or fixing a hot N+1 under load is 10x the effort of designing it right.
!) by default; make a field nullable only when null is a real, meaningful value or when the field's resolver can fail independently and you want partial results instead of a nulled-out parent chain.userErrors list - do not model expected business failures as top-level GraphQL errors.offset: 500000 table scans.edges { node cursor } and pageInfo { hasNextPage endCursor }.first/last with a hard cap - 100 is the standard ceiling; default to 20-25 when the client omits it. An uncapped list argument is a self-serve denial-of-service endpoint.The classic trap: resolving items.customer fires one query per row. Every resolver that crosses a table or service boundary gets a DataLoader, created per-request (a shared loader leaks data across users and requests).
const customerLoader = new DataLoader(async (ids) => {
const rows = await db.customers.whereIn('id', ids);
return ids.map(id => rows.find(r => r.id === id)); // preserve input order; missing = undefined
});
// resolver:
Order.customer = (order) => customerLoader.load(order.customerId);Verify with a query log: a page-of-50-orders query should issue on the order of 2-3 SQL queries, not 51. Auditing an existing codebase for these is n-plus-one-hunter's job.
@deprecated(reason: "use newField") and remove only after client traffic on the field hits zero. Never break a shipped field (deprecation rollout strategy: api-versioning-strategist).@key to define entity references; each subgraph owns its fields and the gateway composes. Keep entities resolvable by their key in every subgraph that extends them.errors array rather than failing the whole response.Bad - tables leaked into the schema, unbounded list, scalar foreign key, throw-only mutation:
type Query {
orders_tbl(user_id: Int): [OrderRow]
}
type OrderRow {
id: Int
status_code: Int # magic number, client must decode
customer_id: Int # scalar FK - client must make a second query
}
type Mutation {
updateOrder(id: Int, status: Int): Boolean # failures = thrown errors only
}Good - domain types, enum, object relationships, capped connection, error-carrying payload:
type Query {
order(id: ID!): Order
orders(first: Int! = 25, after: String): OrderConnection! # first capped at 100
}
type Order {
id: ID!
status: OrderStatus!
items: [LineItem!]!
customer: Customer! # object, resolved via DataLoader
}
enum OrderStatus { PENDING PAID SHIPPED }
type OrderConnection {
edges: [OrderEdge!]!
pageInfo: PageInfo!
}
type Mutation {
updateOrderStatus(input: UpdateOrderStatusInput!): UpdateOrderStatusPayload!
}
type UpdateOrderStatusPayload {
order: Order
userErrors: [UserError!]! # expected failures as data, not exceptions
}
type UserError { field: [String!], code: String!, message: String! }Produce the SDL schema plus a resolver plan listing: every relationship field with its DataLoader, the pagination caps per connection, the depth and complexity limits with the cost model, the authorization rule per sensitive field, and the deprecation policy for future changes.
userErrors so clients can handle them typed._tbl, _id-scalar-FK, or status-code-integer smells remain.userErrors; every error path returns safe messages plus partial results where possible.@key from every extending subgraph.~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.