Skip to content

BlockAnchorReplacer is too loose with single-candidate weak anchors #14046

@jvrdnd

Description

@jvrdnd

Description

Attempts to apply a change run BlockAnchorReplacer, which finds candidates for replacement. If only one candidate is found, the algorithm's threshold for acceptance is 0.0. When one of the anchors is very common (e.g., }), effectively this equates to searching for a single line of matching code, and can replace on the basis of completely hallucinated code. The resulting replacement might even yield invalid code (e.g., replacing the first } when the block closed at a later line).

One possible fix could be to count to count the occurrence of the anchors (in addition to the candidates), and use a higher threshold if one of the anchors is not very unique. Another option (which wouldn't necessarily fix my repro script) would be to compare the block sizes of the search and the match.

Plugins

No response

OpenCode version

1.2.6

Steps to reproduce

import { replace } from "./packages/opencode/src/tool/edit"

const content = `class DatabaseConnection {
  private pool: ConnectionPool

  constructor(config: DbConfig) {
    this.pool = new ConnectionPool(config)
  }

  async query(sql: string, params: unknown[]) {
    const conn = await this.pool.acquire()
    try {
      const result = await conn.execute(sql, params)
      return result.rows
    } finally {
      this.pool.release(conn)
    }
  }

  async transaction<T>(fn: (tx: Transaction) => Promise<T>): Promise<T> {
    const conn = await this.pool.acquire()
    const tx = await conn.beginTransaction()
    try {
      const result = await fn(tx)
      await tx.commit()
      return result
    } catch (e) {
      await tx.rollback()
      throw e
    } finally {
      this.pool.release(conn)
    }
  }
}`

const oldString = `  async query(sql: string, params: unknown[]) {
    validateSqlInjection(sql)
    const sanitized = escapeSqlParams(params)
    const cache = QueryCache.get(sql)
    if (cache) return cache
    const response = await fetch("/api/query", { body: JSON.stringify({ sql, params: sanitized }) })
    const data = await response.json()
    QueryCache.set(sql, data)
    return data
  }`

const newString = `  async query(sql: string, params: unknown[]) {
    validateSqlInjection(sql)
    const sanitized = escapeSqlParams(params)
    const cache = QueryCache.get(sql)
    if (cache) return cache
    logger.debug("Executing query", { sql })
    const response = await fetch("/api/query", { body: JSON.stringify({ sql, params: sanitized }) })
    const data = await response.json()
    QueryCache.set(sql, data)
    return data
  }`

replace(content, oldString, newString)  // should throw but does not

Screenshot and/or share link

No response

Operating System

No response

Terminal

No response

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingcoreAnything pertaining to core functionality of the application (opencode server stuff)

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions